lsm/superpipe

View on GitHub
src/execution.js

Summary

Maintainability
A
45 mins
Test Coverage
import { FN_INPUT } from './pipe'
import { isPlainObject } from './set'

export function executePipe(args, store, pipeState) {
  const { fn, fnName } = pipeState

  if (FN_INPUT === fnName) {
    return fn.call(0, args, store)
  }

  const inputArgs = getInputArgs(store, pipeState, args)
  const injectedFn = getInjectedFunction(store, pipeState, inputArgs)

  if (injectedFn === 0) {
    // Ignored optional pipe, go to next pipe.
    return store.next()
  }

  if (injectedFn || false === injectedFn) {
    pipeState.result = executeInjectedFunc(inputArgs, injectedFn, pipeState)
  } else {
    pipeState.result = fn.apply(0, inputArgs)
  }

  pipeState.fnReturned = true

  // Call set if a plain object was returned
  if (isPlainObject(pipeState.result)) {
    pipeState.set(pipeState.result)
  }

  // Check if we need to run next automatically when:
  // autoNext is true, no error and result is not false.
  if (pipeState.autoNext && !pipeState.error && pipeState.result !== false) {
    store.next()
  }
}

function getInputArgs(store, pipeState, args) {
  if (pipeState.input.length === 0) {
    return args
  }

  const { deps } = pipeState
  return pipeState.input.map(key => {
    if (key === 'next') {
      let called = false
      return function next(err, key, value) {
        if (called) {
          throw new Error(
            '"next" should not be called more than once in a pipe.'
          )
        }
        called = true
        return store.next(err, key, value)
      }
    } else if (key === 'set') {
      // Set function is local to a praticular execution state.
      return pipeState.set
    }

    return getProp(key, store, deps)
  })
}

function getInjectedFunction(store, pipeState, inputArgs) {
  const { fn, deps, fnName } = pipeState

  if (fn !== null || 'string' !== typeof fnName) {
    return
  }

  // An injection pipe. Get the pipe function from the store or the dependency
  // container.
  const injectedFn = getProp(fnName, store, deps)

  if (
    pipeState.optional &&
    (inputArgs.indexOf(undefined) > -1 || typeof injectedFn === 'undefined')
  ) {
    // Optional pipe, go next if any of the dependencies or the function
    // itself is undefined.
    return 0
  }

  return injectedFn
}

/**
 * A function to handle different types of pipe functions.
 * It calls the original function and return the result if that is a function.
 * Or return the result directly for case like `boolean` value.
 */
function executeInjectedFunc(args, injectedFn, pipeState) {
  let result
  /* istanbul ignore next */
  const fnType = typeof injectedFn

  switch (fnType) {
    case 'function':
      // Call it with the arguments passed in when it's a function.
      // We call it with `0` to prevent some JS engines injecting the
      // default `this`.
      result = injectedFn.apply(0, args)
      break
    case 'boolean':
      // Directly return the value when it is a boolean for flow control.
      result = injectedFn
      break
    default:
      // Throw an exception when the original function is not something
      // we understand.
      throw new Error(
        `Pipeline [${pipeState.name}]: Dependency "${
          pipeState.fnName
        }" is not a function or boolean.`
      )
  }

  if ('boolean' === typeof result) {
    // When the result is boolean we will need to consider if it's a `not`
    // pipe and alter the value based on that.
    return pipeState.not ? !result : result
  }

  return result
}

function getProp(key, store, deps) {
  return store.hasOwnProperty(key) ? store[key] : deps[key]
}