Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
exotic-core / src / coerce / toCoerceWrap.ts
Size: Mime:
import { overStaticMethods } from '../../deps'
import {
  fromArgumentsToArray as argumentsToArray,
  toArray,
} from 'exotic/dist/types/array'
import { isObjWithKeys } from 'exotic/dist/types/obj'
import { hasIn } from 'exotic/dist/types/attributes/properties'

const DEBUGS = []
const DEBUG = x => DEBUGS.push(x)
// const toCoercer = x => (isFunction(x) ? x : Typed(x).coerce)

// @TODO should add check to see if IT HAS A DEFAULT first
// then check the `is`
// then use the default accordingly - at least use default when empty?
const toCoercer = x => Typed(x).coerce
const isClassLike = Klass => isObjWithKeys(Klass)

// possible uses
// 1. wrapping function args
// 2. wrapping NTH function arg
// 3. wrapping Class methods (static, proto)
// 4. defaulting ALL or N args, ignoring HOW MANY args are passed in
// 5. coercing & defaulting args ONLY when they are passed in
// 6. coerceIf ?
function Shape(types) {
  // curried, but better api because shape can be arguments style not array
  function wrapFunction(fn) {
    // arity(fn.length)
    // this is the actual wrapping function
    return function coersionWrapper() {
      const args = argumentsToArray.apply(null, arguments)
      // console.log({args}, 'coersionWrapper')
      const evolved = args.map((arg, index) => {
        // const arg = args[index]
        const type = types[index]
        const evolver = toCoercer(type)

        // console.log({
        //   evolver,
        //   arg,
        //   typeof: typeof type,
        //   index,
        //   type,
        //   typed: Typed(type),
        // })
        const evolution = evolver(arg)

        // @NOTE defaulting
        return !evolution && type
          ? type.empty
            ? type.empty(arg, index)
            : type
          : evolution
      })

      return fn.apply(this, evolved)
    }
  }

  const wrapMethods = (shape, Klass) => {
    const updateMethod = (method, methodName, index) => {
      if (hasIn(shape, method)) {
        // DEBUG('shape has method')
        return wrapFunction(shape[method])(method)
      } else {
        // DEBUG('no method for shape')
        return method
      }
    }

    return overStaticMethods(Klass, updateMethod)
  }

  const wrapFunctionOrKlass = (shape, functionOrKlass) => {
    // DEBUG({shape, functionOrKlass})
    if (isClassLike(functionOrKlass)) {
      // DEBUG('isClassLike')
      return wrapMethods(shape, functionOrKlass)
    } else {
      // DEBUG('function')
      return wrapFunction(functionOrKlass)
    }
  }

  // scoped function class
  function shaper(fn) {
    return wrapFunctionOrKlass(types, fn)
  }

  shaper.types = types
  shaper.changeByExample = shaper
  shaper.wrap = wrapFunctionOrKlass
  shaper.wrapMethods = wrapMethods
  shaper.wrapFunction = wrapFunction

  return shaper
}

// @TODO and this will allow shaping a shema with `Typed` first too
const toShape = function() {
  return Shape(
    arguments.length === 1
      ? toArray(arguments[0])
      : argumentsToArray.apply(this, arguments)
  )
}

export { toShape, Shape }
export default toShape