import { ExecutorBuilder, Evaluatable } from "./executor" import * as asyncModule from "async" import * as Leaf from "leaf-ts" import { Errors } from "../errors" export type ValueProvider = ExecutorBuilder | Evaluatable | ((data: TInfo, callback: Callback) => void) | ((data: TInfo, callback: Callback>) => void) | ((data: TInfo) => T) | ((data: TInfo) => Evaluatable) export type Mappable = T[] | { [key: string]: T } export type MapResultOfHandler = TMap extends MapHandler ? TResult[] | { [key: string]: TResult } : never export type MapSourceOfHandler = TMap extends MapHandler ? TSrc[] | { [key: string]: TSrc } : never export type MapHandler = ((data: TItem) => TResult) | ((data: TItem, callback: Callback) => void) | Evaluatable export type MapResultOfHandlers = { [K in keyof TInfo]: MapResultOfHandler } export type MapSourceOfHandlers = { [K in keyof TInfo]: MapSourceOfHandler } export type Mergable = any[] | number | any export namespace ValueProvider { export function isValueProvider(t: any) { if (t instanceof ExecutorBuilder || t instanceof Evaluatable || typeof t == "function") return true return false } export function get(info: TInfo, provider: ValueProvider | any, callback: Callback) { if (!isValueProvider(provider)) { callback(null, provider) return } let todo = provider if (typeof todo == "function") { let fn = todo as Function let ret = fn(info, (err, result) => { if (err) { callback(err) return } if (result instanceof ExecutorBuilder) { result = result.one() } if (result instanceof Evaluatable) { result.evaluate((err, result) => { if (err) { callback(err) return } callback(null, result) }) } callback(null, result) }) if (todo.length < 2) { if (ret instanceof ExecutorBuilder) { ret = ret.one() } if (ret instanceof Evaluatable) { ret.setInfo(info) ret.evaluate((err, result) => { if (err) { callback(err) return } callback(null, result) }) } else { callback(null, ret) } } } else if (todo instanceof Evaluatable || todo instanceof ExecutorBuilder) { if (todo instanceof ExecutorBuilder) { todo = todo.one() } todo.setInfo(info) todo.evaluate((err, result) => { if (err) { callback(err) return } callback(null, result) }) } else { callback(null, todo) } } export function mapValueOf(data: TItem, handler: MapHandler, callback: Callback) { if (handler instanceof Evaluatable) { handler.setInfo(data) handler.evaluate(callback) return } if (!handler) { callback(null, data as any) return } if (handler.length == 2) { handler(data, callback) } else { let result = (handler as any)(data) callback(null, result) } } export function map(src: Mappable, handler: MapHandler, callback: Callback>>) { if (Array.isArray(src)) { let results = [] asyncModule.each(src, (data, done) => { mapValueOf(data, handler, (err, result) => { ValueProvider.get(data, result, (err, result) => { if (err) { done(err) return } results.push(result) done() }) }) }, (err) => { callback(err, results) }) } else { let results = {} let obj = src || {} asyncModule.each(Object.keys(obj), (key, done) => { let data = obj[key] mapValueOf(data, handler, (result) => { ValueProvider.get(data, result, (err, result) => { if (err) { done(err) return } results[key] = result done() }) }) }, (err) => { callback(null, results) }) } } export function mergeComplex(a: T, b: T): T { if (typeof a == "number" || Array.isArray(a)) return ValueProvider.merge(a, b) let result = {} as any for (let key in a) { let value = ValueProvider.merge(a[key], b[key]) result[key] = value } return result } export function merge(a: Mergable, b: Mergable): Mergable { if (Leaf.Util.isEmptyValue(a)) return b if (Leaf.Util.isEmptyValue(b)) return a if (Array.isArray(a) !== Array.isArray(b)) { console.error("Fail to merge", a, b) throw new Errors.LogicError("Can't aggreate array type with a none array type") } if (Array.isArray(a)) { return a.concat(b) } if (typeof a == "number" && typeof b == "number") { return a + b } return a } }