import { Data_Validation_Failed, Data_Coercion_Failed, Superfluous_Data_Field } from '@efforting.tech/errors'; export class Field_Configuration { constructor(validation_function=null, coercion_function=null, factory_function=null, expected_description=undefined) { Object.assign(this, { validation_function, coercion_function, factory_function, expected_description }); } check_validation(value) { const { validation_function } = this; return !validation_function || validation_function(value); } validate(value, target=undefined) { const { validation_function, expected_description } = this; if (!this.check_validation(value)) { throw new Data_Validation_Failed({ validation_function, value, target, expected_description, }); } } coerce(value, target=undefined) { const { coercion_function, expected_description } = this; try { return coercion_function ? coercion_function(value) : value; } catch (e) { throw new Data_Coercion_Failed({ coercion_function, value, target, expected_description, upstream_error: e, }) } } load(value=undefined, target=undefined) { const { factory_function } = this; if ((value === undefined) && factory_function) { value = factory_function(target); } const coerced_value = this.coerce(value, target); this.validate(coerced_value, target); return coerced_value; } } export class Schema { constructor(field_schema, name=undefined) { Object.assign(this, { field_schema, name }); } load(value={}, target=undefined) { const { field_schema, name } = this; for (const [value_name, value_value] of Object.entries(value)) { if (!field_schema[value_name]) { throw new Superfluous_Data_Field({ field_value: value_value, field_name: value_name, target, }); } } const result = {}; const this_schema = name ? `schema "${name}"` : 'untitled schema'; for (const [field_name, field_config] of Object.entries(field_schema)) { const sub_target = { schema: this, name: field_name, config: field_config, parent_target: target, info: `Field "${field_name}" of ${this_schema}` }; result[field_name] = field_config.load(value[field_name], sub_target); } return result; } }