// Copyright (c) 2020 Shellyl_N and Authors // license: ISC // https://github.com/shellyln import { CustomConstraintInfo } from '../types'; import { dummyTargetObject, isUnsafeVarNames } from '../lib/protection'; type MapperFn = (data: any, fields: string[]) => any[]; const mapperErrMsg = 'Unsafe symbol name is appeared in unique constraint assertion:'; const normalMapper: MapperFn = (data: any, fields: string[]) => { const ret: any[] = []; if (0 < fields.length) { for (const field of fields) { if (isUnsafeVarNames(dummyTargetObject, field)) { throw new Error(`${mapperErrMsg} ${field}`); } ret.push(data[field]); } } else { ret.push(data); } return ret; }; const nonNullMapper: MapperFn = (data: any, fields: string[]) => { const ret: any[] = []; if (0 < fields.length) { for (const field of fields) { if (isUnsafeVarNames(dummyTargetObject, field)) { throw new Error(`${mapperErrMsg} ${field}`); } ret.push(data[field] ?? NaN); } } else { ret.push(data); } return ret; }; const checkerGen = (mapper: MapperFn) => { return ((data: any, args: any) => { const errMsg = `evaluateFormula: invalid parameter ${args}`; if (! Array.isArray(data)) { throw new Error(errMsg); } const fields: string[] = []; if (typeof args === 'string') { fields.push(args); } else if (Array.isArray(args)) { for (const z of args) { if (typeof z !== 'string') { throw new Error(errMsg); } } fields.push(...args); } const mapped = data.map(x => mapper(x, fields)); for (let i = 0; i < mapped.length; i++) { CMP: for (let j = 0; j < mapped.length; j++) { if (i === j) { continue; } const a = mapped[i]; const b = mapped[j]; for (let k = 0; k < a.length; k++) { // TODO: this is slow! more better checking if (a[k] !== b[k]) { continue CMP; } } return false; } } return true; }); }; export const constraints: Array<[string, CustomConstraintInfo]> = [ ['unique', { kinds: ['repeated', 'sequence'], check: checkerGen(normalMapper), }], ['unique-non-null', { kinds: ['repeated', 'sequence'], check: checkerGen(nonNullMapper), }], ];