namespace formgen.ioc { /** */ export type type_description = ( { type : "checkbox"; label ?: (null | string); } | { type : "number"; label ?: (null | string); } | { type : "text"; label ?: (null | string); } | { type : "color"; label ?: (null | string); } | { type : "select"; options : Array< { value : string; label : (null | string); } >; label ?: (null | string); } | { type : "list"; element : type_description; label ?: (null | string); } | { type : "group"; members : Record< string, { ioc : type_description; required ?: boolean; } >; label ?: (null | string); } | { type : "sum"; items : Array< { ioc : type_description; kind ?: (null | string); label ?: (null | string); } >; label ?: (null | string); } | { type : "zerez"; } ); /** */ export function from_raw( raw : type_description ) : interface_ioc { switch (raw.type) { case "checkbox": { return ( new class_ioc_checkbox( { "label": (raw.label ?? null), } ) ); break; } case "number": { return ( new class_ioc_number( { "label": (raw.label ?? null), } ) ); break; } case "text": { return ( new class_ioc_text( { "label": (raw.label ?? null), } ) ); break; } case "color": { return ( new class_ioc_color( { "label": (raw.label ?? null), } ) ); break; } case "select": { return ( new class_ioc_select( ( raw.options .map( (option_raw) => ({ "value": option_raw.value, "label": (option_raw.label ?? String(option_raw.value)), }) ) ), { "label": (raw.label ?? null), } ) ); } case "list": { return ( new class_ioc_list( () => from_raw(raw.element), { "label": (raw.label ?? null), } ) ); break; } case "group": { return ( new class_ioc_group( formgen.helpers.list.transform( formgen.helpers.map.to_pairs(raw.members), (pair) => ({ "name": pair.key, "ioc": from_raw(pair.value.ioc), "marked": (pair.value.required ?? false), }) ), { "label": (raw.label ?? null), } ) ); break; } case "sum": { return ( new class_ioc_sum( formgen.helpers.list.transform( raw.items, (raw_item) => ({ "ioc": from_raw(raw_item.ioc), "kind": raw_item.kind, "label": raw_item.label, }) ), { "label": (raw.label ?? null), } ) ); break; } case "zerez": { return ( new class_ioc_zerez( /** * @todo outsource? */ async (id) => { const data_raw : any = await formgen.helpers.zerez.get_unit(id); return { "manufacturer": data_raw["certificate"]["certificate_holder"], "power": data_raw["maximum_active_power_kw"], }; } ) ); break; } default: { throw (new Error("unhandled type " + String(raw))); break; } } } /** * @todo description * @todo if-then-else */ function from_json_schema_inner( defs : Map, root : formgen.helpers.json_schema.type_schema, json_schema : formgen.helpers.json_schema.type_schema, { "fallback_label": fallback_label = null, "map_unhandled_refs_to_empty": map_unhandled_refs_to_empty = false, } : { fallback_label ?: (null | string); map_unhandled_refs_to_empty ?: boolean; } = { } ) : interface_ioc { const schema_adjusted : formgen.helpers.json_schema.type_schema = (() => { if (! ("$ref" in json_schema)) { return json_schema; } else { const cases = [ { "regexp": new RegExp('^#$'), "handling": (match) => { return root; }, }, { "regexp": new RegExp('^#/\\$defs/(.*)$'), "handling": (match) => { const key : string = match[1]; return defs.get(key); }, }, ]; for (const case_ of cases) { const match = json_schema.$ref.match(case_.regexp); if (match === null) { // do nothing } else { return case_.handling(match); } } if (map_unhandled_refs_to_empty) { /** * @todo warning */ return { "type": "null" }; } else { throw (new Error("unhandled ref: " + json_schema.$ref)); } } }) (); if ("enum" in schema_adjusted) { return ( new class_ioc_select( schema_adjusted.enum.map( (value, index) => ( { "value": value, // "label": ("Option " + (index+1).toFixed(0)), "label": JSON.stringify(value), } ) ), { "label": (schema_adjusted.title ?? fallback_label ?? null), } ) ); } else if ("type" in schema_adjusted) { switch (schema_adjusted.type) { case "null": { return ( new class_ioc_empty( ) ); break; } case "boolean": { return ( new class_ioc_checkbox( { "label": (schema_adjusted.title ?? fallback_label ?? null), "hint": (schema_adjusted.description ?? null), } ) ); break; } /** * @todo minimum * @todo exclusiveMinimum * @todo maximum * @todo exclusiveMaximum * @todo multipleOf */ case "integer": { return ( new class_ioc_number( { "label": (schema_adjusted.title ?? fallback_label ?? null), "hint": (schema_adjusted.description ?? null), } ) ); break; } /** * @todo minimum * @todo exclusiveMinimum * @todo maximum * @todo exclusiveMaximum * @todo multipleOf */ case "number": { return ( new class_ioc_number( { "label": (schema_adjusted.title ?? fallback_label ?? null), "hint": (schema_adjusted.description ?? null), } ) ); break; } /** * @todo pattern * @todo minLength * @todo maxLength * @todo more format values */ case "string": { if ("format" in schema_adjusted) { switch (schema_adjusted.format) { case "date": { return ( new class_ioc_date( { "label": (schema_adjusted.title ?? fallback_label ?? null), "hint": (schema_adjusted.description ?? null), } ) ); break; } case "time": { return ( new class_ioc_time( { "label": (schema_adjusted.title ?? fallback_label ?? null), "hint": (schema_adjusted.description ?? null), } ) ); break; } default: { /** * @todo warning */ return ( new class_ioc_text( { "label": (schema_adjusted.title ?? fallback_label ?? null), "hint": (schema_adjusted.description ?? null), } ) ); break; } } } else { return ( new class_ioc_text( { "label": (schema_adjusted.title ?? fallback_label ?? null), "hint": (schema_adjusted.description ?? null), } ) ); } break; } case "array": { if (! ("items" in schema_adjusted)) { throw (new Error("can not generate array input without 'items' definition")); } else { return ( new class_ioc_list( () => from_json_schema_inner( defs, root, (schema_adjusted.items as formgen.helpers.json_schema.type_schema), { "map_unhandled_refs_to_empty": map_unhandled_refs_to_empty, } ), { "label": (schema_adjusted.title ?? fallback_label ?? null), } ) ); } break; } /** * @todo required * @todo additionalProperties * @todo required */ case "object": { if ( ("properties" in schema_adjusted) && ! ( ("additionalProperties" in schema_adjusted) && (schema_adjusted["additionalProperties"] !== false) ) ) { return ( new class_ioc_group( formgen.helpers.list.transform( formgen.helpers.map.to_pairs< formgen.helpers.json_schema.type_schema >(schema_adjusted.properties as Record), (pair : {key : string, value : formgen.helpers.json_schema.type_schema;}) => ({ "name": pair.key, "ioc": from_json_schema_inner( defs, root, pair.value, { "fallback_label": pair.key, "map_unhandled_refs_to_empty": map_unhandled_refs_to_empty, } ), "marked": (schema_adjusted.required ?? []).includes(pair.key), }) ), { "label": (schema_adjusted.title ?? fallback_label ?? null), } ) ); } else if ( ! ("properties" in schema_adjusted) && ( ("additionalProperties" in schema_adjusted) && (schema_adjusted["additionalProperties"] !== false) ) ) { return ( new class_ioc_map( () => from_json_schema_inner( defs, root, (schema_adjusted.additionalProperties as formgen.helpers.json_schema.type_schema), { /** * @todo */ "fallback_label": null, "map_unhandled_refs_to_empty": map_unhandled_refs_to_empty, } ), { "label": (schema_adjusted.title ?? fallback_label ?? null), } ) ); } else { throw (new Error("unhandled object situation")); } break; } default: { throw (new Error("unhandled json schema type: " + JSON.stringify(schema_adjusted))); break; } } } else if ("not" in schema_adjusted) { throw (new Error("not implemented")); } else if ("oneOf" in schema_adjusted) { return ( new class_ioc_sum( formgen.helpers.list.transform;}>( formgen.helpers.list.sequence(schema_adjusted.oneOf.length), (index) => { const sub_schema : formgen.helpers.json_schema.type_schema = schema_adjusted.oneOf[index]; return { "ioc": from_json_schema_inner( defs, root, sub_schema, { "map_unhandled_refs_to_empty": map_unhandled_refs_to_empty, } ), "label": (sub_schema.title ?? fallback_label ?? ("Art " + (index + 1).toFixed(0))), }; } ), { "label": (schema_adjusted.title ?? fallback_label ?? null), } ) ); } else if ("anyOf" in schema_adjusted) { return ( new class_ioc_sum( formgen.helpers.list.transform;}>( formgen.helpers.list.sequence(schema_adjusted.anyOf.length), (index) => { const sub_schema : formgen.helpers.json_schema.type_schema = schema_adjusted.anyOf[index]; return { "ioc": from_json_schema_inner( defs, root, sub_schema, { "map_unhandled_refs_to_empty": map_unhandled_refs_to_empty, } ), "label": (sub_schema.title ?? fallback_label ?? ("Art " + (index + 1).toFixed(0))), }; } ), { "label": (schema_adjusted.title ?? fallback_label ?? null), } ) ); } else if ("allOf" in schema_adjusted) { throw (new Error("not implemented")); } else { throw (new Error("unhandled schema: " + JSON.stringify(schema_adjusted))); } } /** */ export function from_json_schema( root : formgen.helpers.json_schema.type_root, { "map_unhandled_refs_to_empty": map_unhandled_refs_to_empty = false, } : { map_unhandled_refs_to_empty ?: boolean; } = { } ) : interface_ioc { let defs : Map = new Map(); if ("$defs" in root) { for (const [name, value] of Object.entries(root.$defs)) { defs.set(name, value); } } return from_json_schema_inner( defs, root, root, { "map_unhandled_refs_to_empty": map_unhandled_refs_to_empty, } ); } }