javiercejudo/modelico

View on GitHub
src/iterableHelpers.js

Summary

Maintainability
A
3 hrs
Test Coverage
// @flow

import {reviverOrAsIs, equals, haveDifferentTypes, isFunction, mem} from './U'

type Path = Array<string | number>
type BaseListMetadata = Object | Array<Object>

type ListMetadata =
  | BaseListMetadata
  | ((v: any, path: Path) => BaseListMetadata)
//

const tupleItemMetadataGetter = (itemMetadata, v, path) => i =>
  isFunction(itemMetadata[i]) ? itemMetadata[i](v, path) : itemMetadata[i]

const iterableReviverFactory = (IterableType: any, itemMetadata: any) => (
  k,
  v,
  path = []
) => {
  if (k !== '') {
    return v
  }

  const isTuple = Array.isArray(itemMetadata)

  if (isTuple && v.length !== itemMetadata.length) {
    throw TypeError('tuple has missing or extra items')
  }

  const itemMetadataGetter = isTuple
    ? tupleItemMetadataGetter(itemMetadata, v, path)
    : isFunction(itemMetadata) ? _ => itemMetadata(v, path) : _ => itemMetadata

  const revive = (x, i) =>
    reviverOrAsIs(itemMetadataGetter(i))('', x, path.concat(i))

  const iterable = v === null ? null : v.map(revive)

  return new IterableType(iterable)
}

export const iterableMetadata = mem((IterableType: any) =>
  mem((itemMetadata: ListMetadata) => {
    return Object.freeze({
      type: IterableType,
      subtypes: Object.freeze([itemMetadata]),
      reviver: iterableReviverFactory(IterableType, itemMetadata)
    })
  })
)

export const iterableEquals = (
  thisArg: any,
  other: any,
  asUnordered: boolean = false
) => {
  if (thisArg === other) {
    return true
  }

  if (haveDifferentTypes(thisArg, other) || thisArg.size !== other.size) {
    return false
  }

  const thisIter = thisArg[Symbol.iterator]()
  const otherIter = other[Symbol.iterator]()

  for (let i = 0; i < thisArg.size; i += 1) {
    const item = thisIter.next().value

    if (asUnordered) {
      if (!other.has(item)) {
        return false
      }
    } else if (!equals(item, otherIter.next().value)) {
      return false
    }
  }

  return true
}