import {Schema, Field, Fields, CheckFunction, CheckType, CheckObject} from "./Schema"; import {FieldSetUtil} from "./FieldSetUtil"; import {SchemaUtil} from "./SchemaUtil"; import {Validation} from "@anyhowstep/validation"; export class SchemaBuilder { private schema : Schema; constructor (schema : Schema) { this.schema = schema; } public static Create () { return new SchemaBuilder<{}, Desired>({ fields: {} }); } private createFields (key : K, field : Field) : Fields { return Object.assign( {}, this.schema.fields, { [key.toString()] : field } ) as any; } private copyWithField (key : K, field : Field) : Schema { return { allowExtraFields: this.schema.allowExtraFields, check: this.schema.check, fields: this.createFields(key, field) }; } public add (key : K, field : CheckType) : SchemaBuilder; public add (key : K, field : CheckObject) : SchemaBuilder; public add (key : K, field : CheckType | CheckObject) : SchemaBuilder { return new SchemaBuilder(this.copyWithField(key, field)); } public optional (key : K, check : CheckFunction|Schema) : SchemaBuilder { if (FieldSetUtil.IsSchema(check)) { return new SchemaBuilder( this.copyWithField(key, SchemaUtil.GetOptionalCheck(check)) ); } else { return new SchemaBuilder( this.copyWithField(key, Validation.IsOptionalHandler(check)) ); } } public allowExtraFields () : SchemaBuilder { return new SchemaBuilder({ allowExtraFields: true, check: this.schema.check, fields: Object.assign({}, this.schema.fields) }); } public check (this : SchemaBuilder, check : (name : string, target : T) => Promise) : SchemaBuilder { return new SchemaBuilder({ allowExtraFields: this.schema.allowExtraFields, check: check, fields: Object.assign({}, this.schema.fields) as any }); } public build (this : SchemaBuilder) : Schema { return { allowExtraFields: this.schema.allowExtraFields, check: this.schema.check as any, fields: Object.assign({}, this.schema.fields) as any }; } private unionAllowExtraFields (other : Schema) : boolean|undefined { if (this.schema.allowExtraFields == true || other.allowExtraFields == true) { return true; } else { return undefined; } } private unionCheck (other : Schema) : ((name : string, target : T&B) => Promise)|undefined { const myCheck = this.schema.check; const otherCheck = other.check; if (myCheck == null) { if (otherCheck == null) { return undefined; } else { return other.check; } } else { if (otherCheck == null) { return this.schema.check; } else { return (name, target) => { return otherCheck(name, target) .then(() => { return myCheck(name, target); }); }; } } } private unionFields (other : Schema) : Fields { return Object.assign( {}, other.fields, this.schema.fields ) as any; } public union (other : Schema) : SchemaBuilder { return new SchemaBuilder({ allowExtraFields: this.unionAllowExtraFields(other), check: this.unionCheck(other), fields: this.unionFields(other) }); } public partialUnion (other : Schema) : SchemaBuilder, Desired> { return this.union(SchemaUtil.ToPartial(other)); } public remove (key : K) : SchemaBuilder { const fields = Object.assign({}, this.schema.fields); delete fields[key]; return new SchemaBuilder({ allowExtraFields: this.schema.allowExtraFields, check: this.schema.check, fields: fields as any }); } }