import * as asyncModule from "async" import * as Leaf from "leaf-ts" import { Evaluatable } from "./executor" import { Errors } from "../errors" import { ValueProvider, MapHandler, MapResultOfHandler, Mappable, MapSourceOfHandler, MapResultOfHandlers, MapSourceOfHandlers } from "./valueProvider" import * as CoreLogic from "./coreLogic"; export type Category = { [key: string]: { [K in keyof TInfo]: TInfo[K] } } export class Categorizer extends Evaluatable, TInfo> { constructor(public indexes: { [K in keyof TInfo]?: ValueProvider }, public readonly next: MapHandler = (item) => item) { super() } execute(callback: Callback>) { let info = this.info let result = {} asyncModule.each(Object.keys(this.indexes), (name, done) => { let index = this.indexes[name] let datas = info[name] if (!Array.isArray(datas)) { console.error("type", name, "of", datas, "is not array") throw new Errors.LogicError(`Can't categorized none array type '${name}'`) } asyncModule.each(datas, (data, doneData) => { ValueProvider.get(data, index, (err, indexValue: string) => { if (err) { doneData(err) return } let key = indexValue if (typeof index == "string") { key = data[indexValue] } let target = result[indexValue] = result[indexValue] || {} target[name] = target[name] || [] target[name].push(data) doneData() }) }, (err) => { done(err) }) }, (err) => { let mapped = {} asyncModule.each(Object.keys(result), (key, done) => { let data = result[key] ValueProvider.mapValueOf(data, this.next, (err, data) => { if (err) { done(err) return } mapped[key] = data done() }) }, (err) => { callback(err, mapped) }) }) } } export class Joiner extends Evaluatable{ constructor(public readonly path: string = null) { super() } execute(callback: Callback) { let info = this.info let todo = Leaf.Util.getObjectValue(info || {}, this.path) let result = {} for (let key in todo) { result = ValueProvider.mergeComplex(todo[key], result) } callback(null, result as any) } } export class Mapper extends Evaluatable>>{ constructor(public readonly handler: MapHandler) { super() } execute(callback: Callback>>) { let info = this.info || [] ValueProvider.map(this.info, this.handler, callback) } } export class Aggregator } = any> extends Evaluatable, MapSourceOfHandlers> { constructor(public readonly todos: TSolution, public readonly zero: any = null) { super() } execute(callback: Callback>) { let result = this.zero asyncModule.each(Object.keys(this.todos), (aggName, done) => { let info = this.info || {} let agg = this.todos[aggName] let datas = Leaf.Util.getObjectValue(info, aggName) if (!datas) { done() return } if (!agg) { done() return } if (!Array.isArray(datas)) { ValueProvider.mapValueOf(datas, agg, (err, v) => { if (err) { done(err) return } result = this.merge(v, result) done() }) return } ValueProvider.map(datas, agg, (err, items: any[]) => { if (err) { done(err) return } result = this.merge(this.reduce(items), result) done() }) }, (err) => { callback(err, result) }) } reduce(datas: any[]) { let value = null for (let item of datas) { value = this.merge(item, value) } return value } merge(a, b) { if (typeof a == "number" || Array.isArray(a)) return ValueProvider.merge(a, b) if (Leaf.Util.isEmptyValue(b)) return a if (Leaf.Util.isEmptyValue(a)) return b let result = {} let ka = Object.keys(a) let kb = Object.keys(b) let diff = Leaf.Util.arrayDiff(ka, kb) for (let key of diff.left) { result[key] = a[key] } for (let key of diff.right) { result[key] = b[key] } for (let key of diff.intersect) { result[key] = ValueProvider.merge(a[key], b[key]) } return result } }