diff --git a/implementations/javascript/packages/schema/src/interpreter.ts b/implementations/javascript/packages/schema/src/interpreter.ts index 3a29ad3..ad21b9b 100644 --- a/implementations/javascript/packages/schema/src/interpreter.ts +++ b/implementations/javascript/packages/schema/src/interpreter.ts @@ -1,4 +1,5 @@ -import { EncodableDictionary, KeyedDictionary, Dictionary, Value, is, Record, Float, Bytes, isEmbedded, isSequence, Set, Atom, Embedded, merge as plainMerge, Preservable, PreserveWritable, _iterMap, stringify } from '@preserves/core'; +import { EncodableDictionary, KeyedDictionary, Dictionary, Value, is, Record, Float, Bytes, isEmbedded, isSequence, Set, Atom, Embedded, merge as plainMerge, Preservable, PreserveWritable, _iterMap, stringify, fromJS } from '@preserves/core'; +import { SchemaDefinition } from './reflection'; import * as M from './meta'; import * as H from './host'; @@ -15,12 +16,9 @@ export type BindingName = string; export type Bindings = { [key: BindingName]: Parsed }; export type TopBindings = Bindings & Top; -export type SingleConstructor = (input: any) => Parsed; +export type SingleConstructor = ((input: any) => Parsed) & { schema(): SchemaDefinition }; export type MultipleConstructors = { [key: string]: SingleConstructor }; -export type DefinitionConstructors = - | { single: SingleConstructor } - | { multiple: MultipleConstructors } -; +export type DefinitionConstructors = SingleConstructor | MultipleConstructors; export namespace Bindings { export function empty(): Bindings { @@ -111,14 +109,18 @@ export class SchemaInterpreter { } } - _lookup(modulePath: M.ModulePath, name: symbol, f: (d: M.Definition) => R): R { + _lookup( + modulePath: M.ModulePath, + name: symbol, + f: (d: M.Definition, schema: M.Schema) => R, + ): R { const { resolved, schema } = this._findModule(modulePath); return this._withModule(resolved, () => { const definition = schema.definitions.get(name); if (definition === void 0) { throw new Error(`No such preserves-schema definition: ${[... modulePath, name].map(s => s.description!).join('.')}`); } - return f(definition); + return f(definition, schema); }); } @@ -134,7 +136,20 @@ export class SchemaInterpreter { modulePath: M.ModulePath, name: symbol, ): DefinitionConstructors { - return this._lookup(modulePath, name, (definition): DefinitionConstructors => { + return this._lookup(modulePath, name, (definition, schema): DefinitionConstructors => { + function attachSchema( + f: (input: any) => Parsed, + variant?: symbol, + ): SingleConstructor { + const g = f as SingleConstructor; + g.schema = () => ({ + schema: fromJS(schema), + imports: {}, // TODO + definitionName: name, + variant, + }); + return g; + } const ty = H.definitionType(definition); if (ty._variant === 'union') { const multiple: MultipleConstructors = {}; @@ -142,27 +157,38 @@ export class SchemaInterpreter { const _variant = v.label.description!; switch (v.type._variant) { case 'Field': - multiple[_variant] = (value: any) => - this.makeTop(modulePath, name, { _variant, value }); + multiple[_variant] = attachSchema( + (value: any) => this.makeTop(modulePath, name, { _variant, value }), + v.label); break; case 'Record': - multiple[_variant] = (fields: object) => - this.makeTop(modulePath, name, { _variant, ... fields }); + multiple[_variant] = attachSchema( + (fields: object) => this.makeTop(modulePath, name, { _variant, ... fields }), + v.label); break; } }); - return { multiple }; + return multiple; } else { const flatName = M.formatModulePath([... modulePath, name]); switch (ty.value._variant) { case 'Field': { const tmp = { [flatName]: (value: any) => value }; - return { single: tmp[flatName] }; + return attachSchema(tmp[flatName]); } case 'Record': { - const tmp = { [flatName]: (fields: Bindings) => - this.makeTop(modulePath, name, fields) }; - return { single: tmp[flatName] }; + const rec = ty.value.value; + if (rec.fields.length > 1) { + const tmp = { [flatName]: (fields: Bindings) => + this.makeTop(modulePath, name, fields) }; + return attachSchema(tmp[flatName]); + } else { + const tmp = { [flatName]: (field: Parsed) => + this.makeTop(modulePath, name, { + [M.jsId(rec.fields[0].name.description!)]: field, + }) }; + return attachSchema(tmp[flatName]); + } } } }