diff --git a/benchmark/package.json b/benchmark/package.json index f5247648ef..5e855d28f0 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -72,6 +72,6 @@ "suppress-warnings": "^1.0.2", "tstl": "^3.0.0", "uuid": "^9.0.1", - "typia": "../typia-7.0.0-dev.20240921.tgz" + "typia": "../typia-7.0.0-dev.20240922.tgz" } } \ No newline at end of file diff --git a/errors/package.json b/errors/package.json index caa7fe9bc1..1bb74a360a 100644 --- a/errors/package.json +++ b/errors/package.json @@ -32,6 +32,6 @@ "typescript": "^5.3.2" }, "dependencies": { - "typia": "../typia-7.0.0-dev.20240921.tgz" + "typia": "../typia-7.0.0-dev.20240922.tgz" } } \ No newline at end of file diff --git a/package.json b/package.json index dd1162c64d..c6b1eba8c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "typia", - "version": "7.0.0-dev.20240921", + "version": "7.0.0-dev.20240922", "description": "Superfast runtime validators with only one line", "main": "lib/index.js", "typings": "lib/index.d.ts", diff --git a/packages/typescript-json/package.json b/packages/typescript-json/package.json index 9077690757..01707404be 100644 --- a/packages/typescript-json/package.json +++ b/packages/typescript-json/package.json @@ -1,6 +1,6 @@ { "name": "typescript-json", - "version": "7.0.0-dev.20240921", + "version": "7.0.0-dev.20240922", "description": "Superfast runtime validators with only one line", "main": "lib/index.js", "typings": "lib/index.d.ts", @@ -63,7 +63,7 @@ }, "homepage": "https://typia.io", "dependencies": { - "typia": "7.0.0-dev.20240921" + "typia": "7.0.0-dev.20240922" }, "peerDependencies": { "typescript": ">=4.8.0 <5.7.0" diff --git a/src/programmers/notations/NotationGeneralProgrammer.ts b/src/programmers/notations/NotationGeneralProgrammer.ts index e16c3bbb49..3da4b71602 100644 --- a/src/programmers/notations/NotationGeneralProgrammer.ts +++ b/src/programmers/notations/NotationGeneralProgrammer.ts @@ -44,7 +44,7 @@ export namespace NotationGeneralProgrammer { type: ts.Type; name: string | undefined; }): FeatureProgrammer.IDecomposed => { - const config = configure(props.rename)(props.context)(props.importer); + const config = configure(props); if (props.validated === false) config.addition = (collection) => IsProgrammer.write_function_statements(props.context)(props.importer)( @@ -89,231 +89,257 @@ export namespace NotationGeneralProgrammer { }); }; - const write_array_functions = - (config: FeatureProgrammer.IConfig) => - (importer: FunctionImporter) => - (collection: MetadataCollection): ts.VariableStatement[] => - collection - .arrays() - .filter((a) => a.recursive) - .map((type, i) => - StatementFactory.constant( - `${config.prefix}a${i}`, - ts.factory.createArrowFunction( - undefined, - undefined, - FeatureProgrammer.parameterDeclarations(config)( - TypeFactory.keyword("any"), - )(ts.factory.createIdentifier("input")), + const write_array_functions = (props: { + config: FeatureProgrammer.IConfig; + importer: FunctionImporter; + collection: MetadataCollection; + }): ts.VariableStatement[] => + props.collection + .arrays() + .filter((a) => a.recursive) + .map((type, i) => + StatementFactory.constant( + `${props.config.prefix}a${i}`, + ts.factory.createArrowFunction( + undefined, + undefined, + FeatureProgrammer.parameterDeclarations(props.config)( TypeFactory.keyword("any"), - undefined, - decode_array_inline(config)(importer)( - ts.factory.createIdentifier("input"), - MetadataArray.create({ - type, - tags: [], - }), - { - tracable: config.trace, - source: "function", - from: "array", - postfix: "", - }, - ), - ), + )(ts.factory.createIdentifier("input")), + TypeFactory.keyword("any"), + undefined, + decode_array_inline({ + ...props, + input: ts.factory.createIdentifier("input"), + array: MetadataArray.create({ + type, + tags: [], + }), + explore: { + tracable: props.config.trace, + source: "function", + from: "array", + postfix: "", + }, + }), ), - ); + ), + ); - const write_tuple_functions = - (project: ITypiaContext) => - (config: FeatureProgrammer.IConfig) => - (importer: FunctionImporter) => - (collection: MetadataCollection): ts.VariableStatement[] => - collection - .tuples() - .filter((t) => t.recursive) - .map((tuple, i) => - StatementFactory.constant( - `${config.prefix}t${i}`, - ts.factory.createArrowFunction( - undefined, - undefined, - FeatureProgrammer.parameterDeclarations(config)( - TypeFactory.keyword("any"), - )(ts.factory.createIdentifier("input")), + const write_tuple_functions = (props: { + context: ITypiaContext; + config: FeatureProgrammer.IConfig; + importer: FunctionImporter; + collection: MetadataCollection; + }): ts.VariableStatement[] => + props.collection + .tuples() + .filter((t) => t.recursive) + .map((tuple, i) => + StatementFactory.constant( + `${props.config.prefix}t${i}`, + ts.factory.createArrowFunction( + undefined, + undefined, + FeatureProgrammer.parameterDeclarations(props.config)( TypeFactory.keyword("any"), - undefined, - decode_tuple_inline(project)(config)(importer)( - ts.factory.createIdentifier("input"), - tuple, - { - tracable: config.trace, - source: "function", - from: "array", - postfix: "", - }, - ), - ), + )(ts.factory.createIdentifier("input")), + TypeFactory.keyword("any"), + undefined, + decode_tuple_inline({ + ...props, + input: ts.factory.createIdentifier("input"), + tuple, + explore: { + tracable: props.config.trace, + source: "function", + from: "array", + postfix: "", + }, + }), ), - ); + ), + ); /* ----------------------------------------------------------- DECODERS ----------------------------------------------------------- */ - const decode = - (project: ITypiaContext) => - (config: FeatureProgrammer.IConfig) => - (importer: FunctionImporter) => - ( - input: ts.Expression, - meta: Metadata, - explore: FeatureProgrammer.IExplore, - ): ts.Expression => { - // ANY TYPE - if ( - meta.any || - meta.arrays.some((a) => a.type.value.any) || - meta.tuples.some( - (t) => - !!t.type.elements.length && t.type.elements.every((e) => e.any), - ) + const decode = (props: { + context: ITypiaContext; + config: FeatureProgrammer.IConfig; + importer: FunctionImporter; + metadata: Metadata; + explore: FeatureProgrammer.IExplore; + input: ts.Expression; + }): ts.Expression => { + // ANY TYPE + if ( + props.metadata.any || + props.metadata.arrays.some((a) => a.type.value.any) || + props.metadata.tuples.some( + (t) => !!t.type.elements.length && t.type.elements.every((e) => e.any), ) - return ts.factory.createCallExpression(importer.use("any"), undefined, [ - input, - ]); - - interface IUnion { - type: string; - is: () => ts.Expression; - value: () => ts.Expression; - } - const unions: IUnion[] = []; - - //---- - // LIST UP UNION TYPES - //---- - // FUNCTIONAL - if (meta.functions.length) - unions.push({ - type: "functional", - is: () => - ts.factory.createStrictEquality( - ts.factory.createStringLiteral("function"), - ts.factory.createTypeOfExpression(input), - ), - value: () => ts.factory.createIdentifier("undefined"), - }); + ) + return ts.factory.createCallExpression( + props.importer.use("any"), + undefined, + [props.input], + ); - // TUPLES - for (const tuple of meta.tuples) - unions.push({ - type: "tuple", - is: () => - IsProgrammer.decode(project)(importer)( - input, - (() => { - const partial = Metadata.initialize(); - partial.tuples.push(tuple); - return partial; - })(), - explore, - ), - value: () => - decode_tuple(project)(config)(importer)(input, tuple, explore), - }); + interface IUnion { + type: string; + is: () => ts.Expression; + value: () => ts.Expression; + } + const unions: IUnion[] = []; + + //---- + // LIST UP UNION TYPES + //---- + // FUNCTIONAL + if (props.metadata.functions.length) + unions.push({ + type: "functional", + is: () => + ts.factory.createStrictEquality( + ts.factory.createStringLiteral("function"), + ts.factory.createTypeOfExpression(props.input), + ), + value: () => ts.factory.createIdentifier("undefined"), + }); - // ARRAYS - if (meta.arrays.length) - unions.push({ - type: "array", - is: () => ExpressionFactory.isArray(input), - value: () => - explore_arrays(project)(config)(importer)(input, meta.arrays, { - ...explore, + // TUPLES + for (const tuple of props.metadata.tuples) + unions.push({ + type: "tuple", + is: () => + IsProgrammer.decode(props.context)(props.importer)( + props.input, + (() => { + const partial = Metadata.initialize(); + partial.tuples.push(tuple); + return partial; + })(), + props.explore, + ), + value: () => + decode_tuple({ + ...props, + tuple, + }), + }); + + // ARRAYS + if (props.metadata.arrays.length) + unions.push({ + type: "array", + is: () => ExpressionFactory.isArray(props.input), + value: () => + explore_arrays({ + ...props, + elements: props.metadata.arrays, + explore: { + ...props.explore, from: "array", - }), - }); + }, + }), + }); - // NATIVE TYPES - if (meta.sets.length) - unions.push({ - type: "set", - is: () => ExpressionFactory.isInstanceOf("Set")(input), - value: () => - explore_sets(project)(config)(importer)(input, meta.sets, { - ...explore, + // NATIVE TYPES + if (props.metadata.sets.length) + unions.push({ + type: "set", + is: () => ExpressionFactory.isInstanceOf("Set")(props.input), + value: () => + explore_sets({ + ...props, + sets: props.metadata.sets, + explore: { + ...props.explore, from: "array", - }), - }); - if (meta.maps.length) - unions.push({ - type: "map", - is: () => ExpressionFactory.isInstanceOf("Map")(input), - value: () => - explore_maps(project)(config)(importer)(input, meta.maps, { - ...explore, + }, + }), + }); + if (props.metadata.maps.length) + unions.push({ + type: "map", + is: () => ExpressionFactory.isInstanceOf("Map")(props.input), + value: () => + explore_maps({ + ...props, + maps: props.metadata.maps, + explore: { + ...props.explore, from: "array", - }), - }); - for (const native of meta.natives) { - if (native === "WeakSet" || native === "WeakMap") continue; - unions.push({ - type: "native", - is: () => ExpressionFactory.isInstanceOf(native)(input), - value: () => - native === "Boolean" || native === "Number" || native === "String" - ? ts.factory.createCallExpression( - IdentifierFactory.access(input)("valueOf"), - undefined, - undefined, - ) - : decode_native(native)(input), - }); - } - - // OBJECTS - if (meta.objects.length) - unions.push({ - type: "object", - is: () => - ExpressionFactory.isObject({ - checkNull: true, - checkArray: false, - })(input), - value: () => - explore_objects(config)(importer)(input, meta, { - ...explore, - from: "object", - }), - }); - - // COMPOSITION - if (unions.length === 0) return input; - else if (unions.length === 1 && meta.size() === 1) { - const value: ts.Expression = - (meta.nullable || meta.isRequired() === false) && is_instance(meta) - ? ts.factory.createConditionalExpression( - input, + }, + }), + }); + for (const native of props.metadata.natives) { + if (native === "WeakSet" || native === "WeakMap") continue; + unions.push({ + type: "native", + is: () => ExpressionFactory.isInstanceOf(native)(props.input), + value: () => + native === "Boolean" || native === "Number" || native === "String" + ? ts.factory.createCallExpression( + IdentifierFactory.access(props.input)("valueOf"), undefined, - unions[0]!.value(), undefined, - input, ) - : unions[0]!.value(); - return ts.factory.createAsExpression(value, TypeFactory.keyword("any")); - } else { - let last: ts.Expression = input; - for (const u of unions.reverse()) - last = ts.factory.createConditionalExpression( - u.is(), - undefined, - u.value(), - undefined, - last, - ); - return ts.factory.createAsExpression(last, TypeFactory.keyword("any")); - } - }; + : decode_native({ + name: native, + input: props.input, + }), + }); + } + + // OBJECTS + if (props.metadata.objects.length) + unions.push({ + type: "object", + is: () => + ExpressionFactory.isObject({ + checkNull: true, + checkArray: false, + })(props.input), + value: () => + explore_objects({ + ...props, + explore: { + ...props.explore, + from: "object", + }, + }), + }); + + // COMPOSITION + if (unions.length === 0) return props.input; + else if (unions.length === 1 && props.metadata.size() === 1) { + const value: ts.Expression = + (props.metadata.nullable || props.metadata.isRequired() === false) && + is_instance(props.metadata) + ? ts.factory.createConditionalExpression( + props.input, + undefined, + unions[0]!.value(), + undefined, + props.input, + ) + : unions[0]!.value(); + return ts.factory.createAsExpression(value, TypeFactory.keyword("any")); + } else { + let last: ts.Expression = props.input; + for (const u of unions.reverse()) + last = ts.factory.createConditionalExpression( + u.is(), + undefined, + u.value(), + undefined, + last, + ); + return ts.factory.createAsExpression(last, TypeFactory.keyword("any")); + } + }; const decode_object = (importer: FunctionImporter) => FeatureProgrammer.decode_object({ @@ -322,348 +348,403 @@ export namespace NotationGeneralProgrammer { prefix: PREFIX, })(importer); - const decode_array = - (config: FeatureProgrammer.IConfig) => - (importer: FunctionImporter) => - ( - input: ts.Expression, - array: MetadataArray, - explore: FeatureProgrammer.IExplore, - ) => - array.type.recursive - ? ts.factory.createCallExpression( - ts.factory.createIdentifier( - importer.useLocal(`${config.prefix}a${array.type.index}`), + const decode_array = (props: { + config: FeatureProgrammer.IConfig; + importer: FunctionImporter; + input: ts.Expression; + array: MetadataArray; + explore: FeatureProgrammer.IExplore; + }) => + props.array.type.recursive + ? ts.factory.createCallExpression( + ts.factory.createIdentifier( + props.importer.useLocal( + `${props.config.prefix}a${props.array.type.index}`, ), - undefined, - FeatureProgrammer.argumentsArray(config)({ - ...explore, - source: "function", - from: "array", - })(input), - ) - : decode_array_inline(config)(importer)(input, array, explore); - - const decode_array_inline = - (config: FeatureProgrammer.IConfig) => - (importer: FunctionImporter) => - ( - input: ts.Expression, - array: MetadataArray, - explore: FeatureProgrammer.IExplore, - ) => - FeatureProgrammer.decode_array(config)(importer)(NotationJoiner.array)( - input, - array, - explore, - ); + ), + undefined, + FeatureProgrammer.argumentsArray(props.config)({ + ...props.explore, + source: "function", + from: "array", + })(props.input), + ) + : decode_array_inline(props); - const decode_tuple = - (project: ITypiaContext) => - (config: FeatureProgrammer.IConfig) => - (importer: FunctionImporter) => - ( - input: ts.Expression, - tuple: MetadataTuple, - explore: FeatureProgrammer.IExplore, - ): ts.Expression => - tuple.type.recursive - ? ts.factory.createCallExpression( - ts.factory.createIdentifier( - importer.useLocal(`${config.prefix}t${tuple.type.index}`), + const decode_array_inline = (props: { + config: FeatureProgrammer.IConfig; + importer: FunctionImporter; + input: ts.Expression; + array: MetadataArray; + explore: FeatureProgrammer.IExplore; + }) => + FeatureProgrammer.decode_array(props.config)(props.importer)( + NotationJoiner.array, + )(props.input, props.array, props.explore); + + const decode_tuple = (props: { + context: ITypiaContext; + config: FeatureProgrammer.IConfig; + importer: FunctionImporter; + tuple: MetadataTuple; + explore: FeatureProgrammer.IExplore; + input: ts.Expression; + }): ts.Expression => + props.tuple.type.recursive + ? ts.factory.createCallExpression( + ts.factory.createIdentifier( + props.importer.useLocal( + `${props.config.prefix}t${props.tuple.type.index}`, ), - undefined, - FeatureProgrammer.argumentsArray(config)({ - ...explore, - source: "function", - })(input), - ) - : decode_tuple_inline(project)(config)(importer)( - input, - tuple.type, - explore, - ); - - const decode_tuple_inline = - (project: ITypiaContext) => - (config: FeatureProgrammer.IConfig) => - (importer: FunctionImporter) => - ( - input: ts.Expression, - tuple: MetadataTupleType, - explore: FeatureProgrammer.IExplore, - ): ts.Expression => { - const elements: ts.Expression[] = tuple.elements - .filter((m) => m.rest === null) - .map((elem, index) => - decode(project)(config)(importer)( - ts.factory.createElementAccessExpression(input, index), - elem, - { - ...explore, - from: "array", - postfix: explore.postfix.length - ? `${postfix_of_tuple(explore.postfix)}[${index}]"` - : `"[${index}]"`, - }, ), - ); - const rest = (() => { - if (tuple.elements.length === 0) return null; - - const last: Metadata = tuple.elements.at(-1)!; - const rest: Metadata | null = last.rest; - if (rest === null) return null; + undefined, + FeatureProgrammer.argumentsArray(props.config)({ + ...props.explore, + source: "function", + })(props.input), + ) + : decode_tuple_inline({ + ...props, + tuple: props.tuple.type, + }); - return decode(project)(config)(importer)( - ts.factory.createCallExpression( - IdentifierFactory.access(input)("slice"), - undefined, - [ExpressionFactory.number(tuple.elements.length - 1)], - ), - wrap_metadata_rest_tuple(tuple.elements.at(-1)!.rest!), - { - ...explore, - start: tuple.elements.length - 1, + const decode_tuple_inline = (props: { + context: ITypiaContext; + config: FeatureProgrammer.IConfig; + importer: FunctionImporter; + explore: FeatureProgrammer.IExplore; + tuple: MetadataTupleType; + input: ts.Expression; + }): ts.Expression => { + const elements: ts.Expression[] = props.tuple.elements + .filter((m) => m.rest === null) + .map((elem, index) => + decode({ + ...props, + input: ts.factory.createElementAccessExpression(props.input, index), + metadata: elem, + explore: { + ...props.explore, + from: "array", + postfix: props.explore.postfix.length + ? `${postfix_of_tuple(props.explore.postfix)}[${index}]"` + : `"[${index}]"`, }, - ); - })(); - return NotationJoiner.tuple({ - elements, - rest, + }), + ); + const rest = (() => { + if (props.tuple.elements.length === 0) return null; + + const last: Metadata = props.tuple.elements.at(-1)!; + const rest: Metadata | null = last.rest; + if (rest === null) return null; + + return decode({ + ...props, + input: ts.factory.createCallExpression( + IdentifierFactory.access(props.input)("slice"), + undefined, + [ExpressionFactory.number(props.tuple.elements.length - 1)], + ), + metadata: wrap_metadata_rest_tuple(props.tuple.elements.at(-1)!.rest!), + explore: { + ...props.explore, + start: props.tuple.elements.length - 1, + }, }); - }; + })(); + return NotationJoiner.tuple({ + elements, + rest, + }); + }; /* ----------------------------------------------------------- NATIVE CLASSES ----------------------------------------------------------- */ - const decode_native = (type: string) => (input: ts.Expression) => - type === "Date" + const decode_native = (props: { name: string; input: ts.Expression }) => + props.name === "Date" ? ts.factory.createNewExpression( - ts.factory.createIdentifier(type), + ts.factory.createIdentifier(props.name), undefined, - [input], + [props.input], ) - : input; + : props.input; /* ----------------------------------------------------------- EXPLORERS FOR UNION TYPES ----------------------------------------------------------- */ - const explore_sets = - (project: ITypiaContext) => - (config: FeatureProgrammer.IConfig) => - (importer: FunctionImporter) => - ( - input: ts.Expression, - sets: Metadata[], - explore: FeatureProgrammer.IExplore, - ): ts.Expression => - ts.factory.createCallExpression( - UnionExplorer.set({ - checker: IsProgrammer.decode(project)(importer), - decoder: (input, array, explore) => - ts.factory.createNewExpression( - ts.factory.createIdentifier("Set"), - [TypeFactory.keyword("any")], - [decode_array(config)(importer)(input, array, explore)], - ), - empty: ts.factory.createNewExpression( + const explore_sets = (props: { + context: ITypiaContext; + config: FeatureProgrammer.IConfig; + importer: FunctionImporter; + input: ts.Expression; + explore: FeatureProgrammer.IExplore; + sets: Metadata[]; + }): ts.Expression => + ts.factory.createCallExpression( + UnionExplorer.set({ + checker: IsProgrammer.decode(props.context)(props.importer), + decoder: (input, array, explore) => + ts.factory.createNewExpression( ts.factory.createIdentifier("Set"), [TypeFactory.keyword("any")], - [], + [ + decode_array({ + config: props.config, + importer: props.importer, + input, + array, + explore, + }), + ], ), - success: ts.factory.createTrue(), - failure: (input, expected) => - create_throw_error(importer)(expected)(input), - })([])(input, sets, explore), - undefined, - undefined, - ); + empty: ts.factory.createNewExpression( + ts.factory.createIdentifier("Set"), + [TypeFactory.keyword("any")], + [], + ), + success: ts.factory.createTrue(), + failure: (input, expected) => + create_throw_error({ importer: props.importer, expected, input }), + })([])(props.input, props.sets, props.explore), + undefined, + undefined, + ); - const explore_maps = - (project: ITypiaContext) => - (config: FeatureProgrammer.IConfig) => - (importer: FunctionImporter) => - ( - input: ts.Expression, - maps: Metadata.Entry[], - explore: FeatureProgrammer.IExplore, - ): ts.Expression => - ts.factory.createCallExpression( - UnionExplorer.map({ - checker: (top, entry, explore) => { - const func = IsProgrammer.decode(project)(importer); - return ts.factory.createLogicalAnd( - func(ts.factory.createElementAccessExpression(top, 0), entry[0], { - ...explore, - postfix: `${explore.postfix}[0]`, - }), - func(ts.factory.createElementAccessExpression(top, 1), entry[1], { - ...explore, - postfix: `${explore.postfix}[1]`, - }), - ); - }, - decoder: (input, array, explore) => - ts.factory.createNewExpression( - ts.factory.createIdentifier("Map"), - [TypeFactory.keyword("any"), TypeFactory.keyword("any")], - [decode_array(config)(importer)(input, array, explore)], - ), - empty: ts.factory.createNewExpression( + const explore_maps = (props: { + context: ITypiaContext; + config: FeatureProgrammer.IConfig; + importer: FunctionImporter; + input: ts.Expression; + maps: Metadata.Entry[]; + explore: FeatureProgrammer.IExplore; + }): ts.Expression => + ts.factory.createCallExpression( + UnionExplorer.map({ + checker: (top, entry, explore) => { + const func = IsProgrammer.decode(props.context)(props.importer); + return ts.factory.createLogicalAnd( + func(ts.factory.createElementAccessExpression(top, 0), entry[0], { + ...explore, + postfix: `${explore.postfix}[0]`, + }), + func(ts.factory.createElementAccessExpression(top, 1), entry[1], { + ...explore, + postfix: `${explore.postfix}[1]`, + }), + ); + }, + decoder: (input, array, explore) => + ts.factory.createNewExpression( ts.factory.createIdentifier("Map"), [TypeFactory.keyword("any"), TypeFactory.keyword("any")], - [], + [ + decode_array({ + config: props.config, + importer: props.importer, + input, + array, + explore, + }), + ], ), - success: ts.factory.createTrue(), - failure: (input, expected) => - create_throw_error(importer)(expected)(input), - })([])(input, maps, explore), - undefined, - undefined, - ); - - const explore_objects = - (config: FeatureProgrammer.IConfig) => - (importer: FunctionImporter) => - ( - input: ts.Expression, - meta: Metadata, - explore: FeatureProgrammer.IExplore, - ) => { - if (meta.objects.length === 1) - return decode_object(importer)(input, meta.objects[0]!, explore); - - return ts.factory.createCallExpression( - ts.factory.createIdentifier( - importer.useLocal(`${PREFIX}u${meta.union_index!}`), + empty: ts.factory.createNewExpression( + ts.factory.createIdentifier("Map"), + [TypeFactory.keyword("any"), TypeFactory.keyword("any")], + [], ), - undefined, - FeatureProgrammer.argumentsArray(config)(explore)(input), + success: ts.factory.createTrue(), + failure: (input, expected) => + create_throw_error({ importer: props.importer, expected, input }), + })([])(props.input, props.maps, props.explore), + undefined, + undefined, + ); + + const explore_objects = (props: { + config: FeatureProgrammer.IConfig; + importer: FunctionImporter; + input: ts.Expression; + metadata: Metadata; + explore: FeatureProgrammer.IExplore; + }) => { + if (props.metadata.objects.length === 1) + return decode_object(props.importer)( + props.input, + props.metadata.objects[0]!, + props.explore, ); - }; + return ts.factory.createCallExpression( + ts.factory.createIdentifier( + props.importer.useLocal(`${PREFIX}u${props.metadata.union_index!}`), + ), + undefined, + FeatureProgrammer.argumentsArray(props.config)(props.explore)( + props.input, + ), + ); + }; - const explore_arrays = - (project: ITypiaContext) => - (config: FeatureProgrammer.IConfig) => - (importer: FunctionImporter) => - ( - input: ts.Expression, - elements: MetadataArray[], - explore: FeatureProgrammer.IExplore, - ): ts.Expression => - explore_array_like_union_types(config)(importer)( - UnionExplorer.array({ - checker: IsProgrammer.decode(project)(importer), - decoder: decode_array(config)(importer), - empty: ts.factory.createIdentifier("[]"), - success: ts.factory.createTrue(), - failure: (input, expected) => - create_throw_error(importer)(expected)(input), - }), - )(input, elements, explore); - - const explore_array_like_union_types = - (config: FeatureProgrammer.IConfig) => - (importer: FunctionImporter) => - ( - factory: ( - parameters: ts.ParameterDeclaration[], - ) => ( - input: ts.Expression, - elements: T[], - explore: FeatureProgrammer.IExplore, - ) => ts.ArrowFunction, - ) => - ( + const explore_arrays = (props: { + context: ITypiaContext; + config: FeatureProgrammer.IConfig; + importer: FunctionImporter; + input: ts.Expression; + elements: MetadataArray[]; + explore: FeatureProgrammer.IExplore; + }): ts.Expression => + explore_array_like_union_types({ + ...props, + factory: UnionExplorer.array({ + checker: IsProgrammer.decode(props.context)(props.importer), + decoder: (input, array, explore) => + decode_array({ + config: props.config, + importer: props.importer, + input, + array, + explore, + }), + empty: ts.factory.createIdentifier("[]"), + success: ts.factory.createTrue(), + failure: (input, expected) => + create_throw_error({ + importer: props.importer, + expected, + input, + }), + }), + }); + + const explore_array_like_union_types = < + T extends MetadataArray | MetadataTuple, + >(props: { + config: FeatureProgrammer.IConfig; + importer: FunctionImporter; + factory: ( + parameters: ts.ParameterDeclaration[], + ) => ( input: ts.Expression, elements: T[], explore: FeatureProgrammer.IExplore, - ): ts.Expression => { - const arrow = - (parameters: ts.ParameterDeclaration[]) => - (explore: FeatureProgrammer.IExplore) => - (input: ts.Expression): ts.ArrowFunction => - factory(parameters)(input, elements, explore); - if (elements.every((e) => e.type.recursive === false)) - ts.factory.createCallExpression( - arrow([])(explore)(input), - undefined, - [], - ); - - explore = { - ...explore, - source: "function", - from: "array", - }; - return ts.factory.createCallExpression( - ts.factory.createIdentifier( - importer.emplaceUnion( - config.prefix, - elements.map((e) => e.type.name).join(" | "), - () => - arrow( - FeatureProgrammer.parameterDeclarations(config)( - TypeFactory.keyword("any"), - )(ts.factory.createIdentifier("input")), - )({ - ...explore, - postfix: "", - })(ts.factory.createIdentifier("input")), - ), - ), + ) => ts.ArrowFunction; + input: ts.Expression; + elements: T[]; + explore: FeatureProgrammer.IExplore; + }): ts.Expression => { + const arrow = + (parameters: ts.ParameterDeclaration[]) => + (explore: FeatureProgrammer.IExplore) => + (input: ts.Expression): ts.ArrowFunction => + props.factory(parameters)(input, props.elements, explore); + if (props.elements.every((e) => e.type.recursive === false)) + ts.factory.createCallExpression( + arrow([])(props.explore)(props.input), undefined, - FeatureProgrammer.argumentsArray(config)(explore)(input), + [], ); + + const explore: FeatureProgrammer.IExplore = { + ...props.explore, + source: "function", + from: "array", }; + return ts.factory.createCallExpression( + ts.factory.createIdentifier( + props.importer.emplaceUnion( + props.config.prefix, + props.elements.map((e) => e.type.name).join(" | "), + () => + arrow( + FeatureProgrammer.parameterDeclarations(props.config)( + TypeFactory.keyword("any"), + )(ts.factory.createIdentifier("input")), + )({ + ...explore, + postfix: "", + })(ts.factory.createIdentifier("input")), + ), + ), + undefined, + FeatureProgrammer.argumentsArray(props.config)(explore)(props.input), + ); + }; /* ----------------------------------------------------------- CONFIGURATIONS ----------------------------------------------------------- */ const PREFIX = "$c"; - const configure = - (rename: (str: string) => string) => - (project: ITypiaContext) => - (importer: FunctionImporter): FeatureProgrammer.IConfig => { - const config: FeatureProgrammer.IConfig = { - types: { - input: (type, name) => - ts.factory.createTypeReferenceNode( - name ?? TypeFactory.getFullName(project.checker)(type), - ), - output: (type, name) => - ts.factory.createTypeReferenceNode( - returnType(rename)( - name ?? TypeFactory.getFullName(project.checker)(type), - ), + const configure = (props: { + rename: (str: string) => string; + context: ITypiaContext; + importer: FunctionImporter; + }): FeatureProgrammer.IConfig => { + const config: FeatureProgrammer.IConfig = { + types: { + input: (type, name) => + ts.factory.createTypeReferenceNode( + name ?? TypeFactory.getFullName(props.context.checker)(type), + ), + output: (type, name) => + ts.factory.createTypeReferenceNode( + returnType(props.rename)( + name ?? TypeFactory.getFullName(props.context.checker)(type), ), - }, - prefix: PREFIX, - trace: false, - path: false, - initializer, - decoder: () => decode(project)(config)(importer), - objector: { - checker: () => IsProgrammer.decode(project)(importer), - decoder: () => decode_object(importer), - joiner: NotationJoiner.object(rename), - unionizer: decode_union_object( - IsProgrammer.decode_object(project)(importer), - )(decode_object(importer))((exp) => exp)((input, expected) => - create_throw_error(importer)(expected)(input), ), - failure: (input, expected) => - create_throw_error(importer)(expected)(input), - }, - generator: { - arrays: () => write_array_functions(config)(importer), - tuples: () => write_tuple_functions(project)(config)(importer), - }, - }; - return config; + }, + prefix: PREFIX, + trace: false, + path: false, + initializer, + decoder: () => (input, metadata, explore) => + decode({ + context: props.context, + config, + importer: props.importer, + metadata, + explore, + input, + }), + objector: { + checker: () => IsProgrammer.decode(props.context)(props.importer), + decoder: () => decode_object(props.importer), + joiner: NotationJoiner.object(props.rename), + unionizer: decode_union_object( + IsProgrammer.decode_object(props.context)(props.importer), + )(decode_object(props.importer))((exp) => exp)((input, expected) => + create_throw_error({ + importer: props.importer, + expected, + input, + }), + ), + failure: (input, expected) => + create_throw_error({ + importer: props.importer, + expected, + input, + }), + }, + generator: { + arrays: () => (collection) => + write_array_functions({ + importer: props.importer, + config, + collection, + }), + tuples: () => (collection) => + write_tuple_functions({ + context: props.context, + importer: props.importer, + config, + collection, + }), + }, }; + return config; + }; const initializer: FeatureProgrammer.IConfig["initializer"] = (project) => (importer) => (type) => { @@ -686,28 +767,29 @@ export namespace NotationGeneralProgrammer { return [collection, result.data]; }; - const create_throw_error = - (importer: FunctionImporter) => - (expected: string) => - (value: ts.Expression) => - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - importer.use("throws"), - [], - [ - ts.factory.createObjectLiteralExpression( - [ - ts.factory.createPropertyAssignment( - "expected", - ts.factory.createStringLiteral(expected), - ), - ts.factory.createPropertyAssignment("value", value), - ], - true, - ), - ], - ), - ); + const create_throw_error = (props: { + importer: FunctionImporter; + expected: string; + input: ts.Expression; + }) => + ts.factory.createExpressionStatement( + ts.factory.createCallExpression( + props.importer.use("throws"), + [], + [ + ts.factory.createObjectLiteralExpression( + [ + ts.factory.createPropertyAssignment( + "expected", + ts.factory.createStringLiteral(props.expected), + ), + ts.factory.createPropertyAssignment("value", props.input), + ], + true, + ), + ], + ), + ); const is_instance = (meta: Metadata): boolean => !!meta.objects.length || diff --git a/src/programmers/protobuf/ProtobufDecodeProgrammer.ts b/src/programmers/protobuf/ProtobufDecodeProgrammer.ts index 0e90b964ed..691eac17f6 100644 --- a/src/programmers/protobuf/ProtobufDecodeProgrammer.ts +++ b/src/programmers/protobuf/ProtobufDecodeProgrammer.ts @@ -43,12 +43,16 @@ export namespace ProtobufDecodeProgrammer { functions: Object.fromEntries( collection .objects() - .filter((obj) => ProtobufUtil.isStaticObject(obj)) - .map((obj) => [ - `${PREFIX}o${obj.index}`, + .filter((object) => ProtobufUtil.isStaticObject(object)) + .map((object) => [ + `${PREFIX}o${object.index}`, StatementFactory.constant( - props.importer.useLocal(`${PREFIX}o${obj.index}`), - write_object_function(props.context)(props.importer)(obj), + props.importer.useLocal(`${PREFIX}o${object.index}`), + write_object_function({ + context: props.context, + importer: props.importer, + object, + }), ), ]), ), @@ -87,7 +91,10 @@ export namespace ProtobufDecodeProgrammer { ), ), ts.factory.createReturnStatement( - decode_regular_object(true)(meta.objects[0]!), + decode_regular_object({ + top: true, + object: meta.objects[0]!, + }), ), ], true, @@ -111,153 +118,163 @@ export namespace ProtobufDecodeProgrammer { }); }; - const write_object_function = - (project: ITypiaContext) => - (importer: FunctionImporter) => - (obj: MetadataObject): ts.ArrowFunction => - ts.factory.createArrowFunction( - undefined, - undefined, + const write_object_function = (props: { + context: ITypiaContext; + importer: FunctionImporter; + object: MetadataObject; + }): ts.ArrowFunction => + ts.factory.createArrowFunction( + undefined, + undefined, + [ + IdentifierFactory.parameter("reader"), + IdentifierFactory.parameter( + "length", + TypeFactory.keyword("number"), + ExpressionFactory.number(-1), + ), + ], + TypeFactory.keyword("any"), + undefined, + ts.factory.createBlock( [ - IdentifierFactory.parameter("reader"), - IdentifierFactory.parameter( - "length", - TypeFactory.keyword("number"), - ExpressionFactory.number(-1), - ), - ], - TypeFactory.keyword("any"), - undefined, - ts.factory.createBlock( - [ - ts.factory.createExpressionStatement( - ts.factory.createBinaryExpression( - ts.factory.createIdentifier("length"), - ts.factory.createToken(ts.SyntaxKind.EqualsToken), - ts.factory.createConditionalExpression( - ts.factory.createLessThan( - ts.factory.createIdentifier("length"), - ExpressionFactory.number(0), - ), + ts.factory.createExpressionStatement( + ts.factory.createBinaryExpression( + ts.factory.createIdentifier("length"), + ts.factory.createToken(ts.SyntaxKind.EqualsToken), + ts.factory.createConditionalExpression( + ts.factory.createLessThan( + ts.factory.createIdentifier("length"), + ExpressionFactory.number(0), + ), + undefined, + ts.factory.createCallExpression( + IdentifierFactory.access(READER())("size"), undefined, + undefined, + ), + undefined, + ts.factory.createAdd( ts.factory.createCallExpression( - IdentifierFactory.access(READER())("size"), + IdentifierFactory.access(READER())("index"), undefined, undefined, ), - undefined, - ts.factory.createAdd( - ts.factory.createCallExpression( - IdentifierFactory.access(READER())("index"), - undefined, - undefined, - ), - ts.factory.createIdentifier("length"), - ), + ts.factory.createIdentifier("length"), ), ), ), - ...write_object_function_body(project)(importer)({ - condition: ts.factory.createLessThan( - ts.factory.createCallExpression( - IdentifierFactory.access(READER())("index"), - undefined, - undefined, - ), - ts.factory.createIdentifier("length"), + ), + ...write_object_function_body({ + context: props.context, + importer: props.importer, + condition: ts.factory.createLessThan( + ts.factory.createCallExpression( + IdentifierFactory.access(READER())("index"), + undefined, + undefined, ), - tag: "tag", - output: "output", - })(obj.properties), - ts.factory.createReturnStatement( - ts.factory.createIdentifier("output"), + ts.factory.createIdentifier("length"), ), - ], - true, - ), - ); + tag: "tag", + output: "output", + properties: props.object.properties, + }), + ts.factory.createReturnStatement( + ts.factory.createIdentifier("output"), + ), + ], + true, + ), + ); - const write_object_function_body = - (project: ITypiaContext) => - (importer: FunctionImporter) => - (props: { condition: ts.Expression; tag: string; output: string }) => - (properties: MetadataProperty[]): ts.Statement[] => { - let i: number = 1; - const clauses: ts.CaseClause[] = properties - .map((p) => { - const clause = decode_property(project)(importer)(i)( - IdentifierFactory.access(ts.factory.createIdentifier(props.output))( - p.key.getSoleLiteral()!, - ), - p.value, - ); - i += ProtobufUtil.size(p.value); - return clause; - }) - .flat(); - return [ - StatementFactory.constant( - props.output, - ts.factory.createAsExpression( - ts.factory.createObjectLiteralExpression( - properties - .filter( - (p) => - !( - project.compilerOptions.exactOptionalPropertyTypes === - true && p.value.optional === true - ), - ) - .map((p) => - ts.factory.createPropertyAssignment( - IdentifierFactory.identifier(p.key.getSoleLiteral()!), - write_property_default_value(p.value), + const write_object_function_body = (props: { + context: ITypiaContext; + importer: FunctionImporter; + condition: ts.Expression; + tag: string; + output: string; + properties: MetadataProperty[]; + }): ts.Statement[] => { + let index: number = 1; + const clauses: ts.CaseClause[] = props.properties + .map((p) => { + const clause = decode_property({ + context: props.context, + importer: props.importer, + index, + accessor: IdentifierFactory.access( + ts.factory.createIdentifier(props.output), + )(p.key.getSoleLiteral()!), + metadata: p.value, + }); + index += ProtobufUtil.size(p.value); + return clause; + }) + .flat(); + return [ + StatementFactory.constant( + props.output, + ts.factory.createAsExpression( + ts.factory.createObjectLiteralExpression( + props.properties + .filter( + (p) => + !( + props.context.compilerOptions.exactOptionalPropertyTypes === + true && p.value.optional === true ), + ) + .map((p) => + ts.factory.createPropertyAssignment( + IdentifierFactory.identifier(p.key.getSoleLiteral()!), + write_property_default_value(p.value), ), - true, - ), - TypeFactory.keyword("any"), + ), + true, ), + TypeFactory.keyword("any"), ), - ts.factory.createWhileStatement( - props.condition, - ts.factory.createBlock([ - StatementFactory.constant( - props.tag, - ts.factory.createCallExpression( - IdentifierFactory.access(READER())("uint32"), - undefined, - undefined, - ), + ), + ts.factory.createWhileStatement( + props.condition, + ts.factory.createBlock([ + StatementFactory.constant( + props.tag, + ts.factory.createCallExpression( + IdentifierFactory.access(READER())("uint32"), + undefined, + undefined, ), - ts.factory.createSwitchStatement( - ts.factory.createUnsignedRightShift( - ts.factory.createIdentifier(props.tag), - ExpressionFactory.number(3), - ), - ts.factory.createCaseBlock([ - ...clauses, - ts.factory.createDefaultClause([ - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - IdentifierFactory.access(READER())("skipType"), - undefined, - [ - ts.factory.createBitwiseAnd( - ts.factory.createIdentifier(props.tag), - ExpressionFactory.number(7), - ), - ], - ), + ), + ts.factory.createSwitchStatement( + ts.factory.createUnsignedRightShift( + ts.factory.createIdentifier(props.tag), + ExpressionFactory.number(3), + ), + ts.factory.createCaseBlock([ + ...clauses, + ts.factory.createDefaultClause([ + ts.factory.createExpressionStatement( + ts.factory.createCallExpression( + IdentifierFactory.access(READER())("skipType"), + undefined, + [ + ts.factory.createBitwiseAnd( + ts.factory.createIdentifier(props.tag), + ExpressionFactory.number(7), + ), + ], ), - ts.factory.createBreakStatement(), - ]), + ), + ts.factory.createBreakStatement(), ]), - ), - ]), - ), - ]; - }; + ]), + ), + ]), + ), + ]; + }; const write_property_default_value = (value: Metadata) => ts.factory.createAsExpression( @@ -303,87 +320,118 @@ export namespace ProtobufDecodeProgrammer { /* ----------------------------------------------------------- DECODERS ----------------------------------------------------------- */ - const decode_property = - (project: ITypiaContext) => - (importer: FunctionImporter) => - (index: number) => - ( - accessor: ts.ElementAccessExpression | ts.PropertyAccessExpression, - meta: Metadata, - ): ts.CaseClause[] => { - const clauses: ts.CaseClause[] = []; - const emplace = (name: string) => (v: ts.Expression | ts.Statement[]) => - clauses.push( - ts.factory.createCaseClause( - ExpressionFactory.number(index++), - Array.isArray(v) - ? [ - ts.factory.createExpressionStatement( - ts.factory.createIdentifier(`// type: ${name}`), - ), - ...v, - ts.factory.createBreakStatement(), - ] - : [ - ts.factory.createExpressionStatement( - ts.factory.createIdentifier(`// ${name}`), - ), - ts.factory.createExpressionStatement( - ts.factory.createBinaryExpression( - accessor, - ts.factory.createToken(ts.SyntaxKind.EqualsToken), - v, - ), + const decode_property = (props: { + context: ITypiaContext; + importer: FunctionImporter; + index: number; + accessor: ts.ElementAccessExpression | ts.PropertyAccessExpression; + metadata: Metadata; + }): ts.CaseClause[] => { + const clauses: ts.CaseClause[] = []; + const emplace = (name: string, value: ts.Expression | ts.Statement[]) => + clauses.push( + ts.factory.createCaseClause( + ExpressionFactory.number(props.index++), + Array.isArray(value) + ? [ + ts.factory.createExpressionStatement( + ts.factory.createIdentifier(`// type: ${name}`), + ), + ...value, + ts.factory.createBreakStatement(), + ] + : [ + ts.factory.createExpressionStatement( + ts.factory.createIdentifier(`// ${name}`), + ), + ts.factory.createExpressionStatement( + ts.factory.createBinaryExpression( + props.accessor, + ts.factory.createToken(ts.SyntaxKind.EqualsToken), + value, ), - ts.factory.createBreakStatement(), - ], - ), - ); + ), + ts.factory.createBreakStatement(), + ], + ), + ); - const required: boolean = meta.isRequired() && !meta.nullable; - for (const atomic of ProtobufUtil.getAtomics(meta)) - emplace(atomic)(decode_atomic(meta)(atomic)); - if (meta.natives.length) emplace("bytes")(decode_bytes("bytes")); - for (const array of meta.arrays) - emplace(`Array<${array.type.value.getName()}>`)( - decode_array(accessor, array, required), - ); - for (const map of meta.maps) - emplace(`Map`)( - decode_map(project)(importer)(accessor, map, required), - ); - for (const obj of meta.objects) - emplace(obj.name)( - ProtobufUtil.isStaticObject(obj) - ? decode_regular_object(false)(obj) - : decode_dynamic_object(project)(importer)(accessor, obj, required), - ); - return clauses; - }; + const required: boolean = + props.metadata.isRequired() && !props.metadata.nullable; + for (const atomic of ProtobufUtil.getAtomics(props.metadata)) + emplace( + atomic, + decode_atomic({ + metadata: props.metadata, + type: atomic, + }), + ); + if (props.metadata.natives.length) emplace("bytes", decode_bytes("bytes")); + for (const array of props.metadata.arrays) + emplace( + `Array<${array.type.value.getName()}>`, + decode_array({ + accessor: props.accessor, + array, + required, + }), + ); + for (const entry of props.metadata.maps) + emplace( + `Map`, + decode_map({ + context: props.context, + importer: props.importer, + accessor: props.accessor, + entry, + required, + }), + ); + for (const object of props.metadata.objects) + emplace( + object.name, + ProtobufUtil.isStaticObject(object) + ? decode_regular_object({ + top: false, + object, + }) + : decode_dynamic_object({ + context: props.context, + importer: props.importer, + accessor: props.accessor, + object, + required, + }), + ); + return clauses; + }; - const decode_atomic = - (meta: Metadata) => - (atomic: ProtobufAtomic): ts.Expression => { - if (atomic === "string") return decode_bytes("string"); + const decode_atomic = (props: { + metadata: Metadata; + type: ProtobufAtomic; + }): ts.Expression => { + if (props.type === "string") return decode_bytes("string"); - const call: ts.CallExpression = ts.factory.createCallExpression( - IdentifierFactory.access(ts.factory.createIdentifier("reader"))(atomic), - undefined, - undefined, - ); - if (atomic !== "int64" && atomic !== "uint64") return call; + const call: ts.CallExpression = ts.factory.createCallExpression( + IdentifierFactory.access(ts.factory.createIdentifier("reader"))( + props.type, + ), + undefined, + undefined, + ); + if (props.type !== "int64" && props.type !== "uint64") return call; - const isNumber: boolean = ProtobufUtil.getNumbers(meta).some( - (n) => n === atomic, - ); - return isNumber - ? ts.factory.createCallExpression( - ts.factory.createIdentifier("Number"), - undefined, - [call], - ) - : call; - }; + const isNumber: boolean = ProtobufUtil.getNumbers(props.metadata).some( + (n) => n === props.type, + ); + return isNumber + ? ts.factory.createCallExpression( + ts.factory.createIdentifier("Number"), + undefined, + [call], + ) + : call; + }; const decode_bytes = (method: "bytes" | "string"): ts.Expression => ts.factory.createCallExpression( @@ -392,16 +440,16 @@ export namespace ProtobufDecodeProgrammer { undefined, ); - const decode_array = ( - accessor: ts.ElementAccessExpression | ts.PropertyAccessExpression, - array: MetadataArray, - required: boolean, - ): ts.Statement[] => { + const decode_array = (props: { + accessor: ts.ElementAccessExpression | ts.PropertyAccessExpression; + array: MetadataArray; + required: boolean; + }): ts.Statement[] => { const statements: Array = []; - if (required === false) + if (props.required === false) statements.push( ts.factory.createBinaryExpression( - accessor, + props.accessor, ts.factory.createToken(ts.SyntaxKind.QuestionQuestionEqualsToken), ts.factory.createAsExpression( ts.factory.createArrayLiteralExpression(), @@ -409,13 +457,21 @@ export namespace ProtobufDecodeProgrammer { ), ), ); - const atomics = ProtobufUtil.getAtomics(array.type.value); + const atomics = ProtobufUtil.getAtomics(props.array.type.value); const decoder = atomics.length - ? () => decode_atomic(array.type.value)(atomics[0]!) - : array.type.value.natives.length + ? () => + decode_atomic({ + metadata: props.array.type.value, + type: atomics[0]!, + }) + : props.array.type.value.natives.length ? () => decode_bytes("bytes") - : array.type.value.objects.length - ? () => decode_regular_object(false)(array.type.value.objects[0]!) + : props.array.type.value.objects.length + ? () => + decode_regular_object({ + top: false, + object: props.array.type.value.objects[0]!, + }) : null; if (decoder === null) throw new Error("Never reach here."); else if (atomics.length && atomics[0] !== "string") { @@ -456,7 +512,7 @@ export namespace ProtobufDecodeProgrammer { ), ts.factory.createExpressionStatement( ts.factory.createCallExpression( - IdentifierFactory.access(accessor)("push"), + IdentifierFactory.access(props.accessor)("push"), undefined, [decoder()], ), @@ -467,7 +523,7 @@ export namespace ProtobufDecodeProgrammer { ), ts.factory.createExpressionStatement( ts.factory.createCallExpression( - IdentifierFactory.access(accessor)("push"), + IdentifierFactory.access(props.accessor)("push"), undefined, [decoder()], ), @@ -477,7 +533,7 @@ export namespace ProtobufDecodeProgrammer { } else statements.push( ts.factory.createCallExpression( - IdentifierFactory.access(accessor)("push"), + IdentifierFactory.access(props.accessor)("push"), undefined, [decoder()], ), @@ -487,159 +543,166 @@ export namespace ProtobufDecodeProgrammer { ); }; - const decode_regular_object = - (top: boolean) => - (obj: MetadataObject): ts.Expression => - ts.factory.createCallExpression( - ts.factory.createIdentifier(`${PREFIX}o${obj.index}`), - undefined, - [ - ts.factory.createIdentifier("reader"), - ...(top - ? [] - : [ - ts.factory.createCallExpression( - IdentifierFactory.access(READER())("uint32"), - undefined, - undefined, - ), - ]), - ], - ); + const decode_regular_object = (props: { + top: boolean; + object: MetadataObject; + }): ts.Expression => + ts.factory.createCallExpression( + ts.factory.createIdentifier(`${PREFIX}o${props.object.index}`), + undefined, + [ + ts.factory.createIdentifier("reader"), + ...(props.top + ? [] + : [ + ts.factory.createCallExpression( + IdentifierFactory.access(READER())("uint32"), + undefined, + undefined, + ), + ]), + ], + ); - const decode_dynamic_object = - (project: ITypiaContext) => - (importer: FunctionImporter) => - ( - accessor: ts.ElementAccessExpression | ts.PropertyAccessExpression, - obj: MetadataObject, - required: boolean, - ): ts.Statement[] => { - const top = obj.properties[0]!; - return decode_entry(project)(importer)({ - initializer: () => - ts.factory.createBinaryExpression( - accessor, - ts.factory.createToken(ts.SyntaxKind.QuestionQuestionEqualsToken), - ts.factory.createObjectLiteralExpression(), - ), - setter: () => - ts.factory.createBinaryExpression( - ts.factory.createElementAccessExpression( - accessor, - ts.factory.createIdentifier("entry.key"), - ), - ts.factory.createToken(ts.SyntaxKind.EqualsToken), - ts.factory.createIdentifier("entry.value"), + const decode_dynamic_object = (props: { + context: ITypiaContext; + importer: FunctionImporter; + accessor: ts.ElementAccessExpression | ts.PropertyAccessExpression; + object: MetadataObject; + required: boolean; + }): ts.Statement[] => { + const top: MetadataProperty = props.object.properties[0]!; + return decode_entry({ + context: props.context, + importer: props.importer, + initializer: () => + ts.factory.createBinaryExpression( + props.accessor, + ts.factory.createToken(ts.SyntaxKind.QuestionQuestionEqualsToken), + ts.factory.createObjectLiteralExpression(), + ), + setter: () => + ts.factory.createBinaryExpression( + ts.factory.createElementAccessExpression( + props.accessor, + ts.factory.createIdentifier("entry.key"), ), - })( - MetadataProperty.create({ - ...top, - key: (() => { - const key: Metadata = Metadata.initialize(); - key.atomics.push( - MetadataAtomic.create({ - type: "string", - tags: [], - }), - ); - return key; - })(), - }), - required, - ); - }; + ts.factory.createToken(ts.SyntaxKind.EqualsToken), + ts.factory.createIdentifier("entry.value"), + ), + entry: MetadataProperty.create({ + ...top, + key: (() => { + const key: Metadata = Metadata.initialize(); + key.atomics.push( + MetadataAtomic.create({ + type: "string", + tags: [], + }), + ); + return key; + })(), + }), + required: props.required, + }); + }; - const decode_map = - (project: ITypiaContext) => - (importer: FunctionImporter) => - ( - accessor: ts.ElementAccessExpression | ts.PropertyAccessExpression, - map: Metadata.Entry, - required: boolean, - ): ts.Statement[] => - decode_entry(project)(importer)({ - initializer: () => - ts.factory.createBinaryExpression( - accessor, - ts.factory.createToken(ts.SyntaxKind.QuestionQuestionEqualsToken), - ts.factory.createNewExpression( - ts.factory.createIdentifier("Map"), - [TypeFactory.keyword("any"), TypeFactory.keyword("any")], - [], - ), + const decode_map = (props: { + context: ITypiaContext; + importer: FunctionImporter; + accessor: ts.ElementAccessExpression | ts.PropertyAccessExpression; + entry: Metadata.Entry; + required: boolean; + }): ts.Statement[] => + decode_entry({ + context: props.context, + importer: props.importer, + initializer: () => + ts.factory.createBinaryExpression( + props.accessor, + ts.factory.createToken(ts.SyntaxKind.QuestionQuestionEqualsToken), + ts.factory.createNewExpression( + ts.factory.createIdentifier("Map"), + [TypeFactory.keyword("any"), TypeFactory.keyword("any")], + [], ), - setter: () => + ), + setter: () => + ts.factory.createCallExpression( + IdentifierFactory.access(props.accessor)("set"), + undefined, + [ + ts.factory.createIdentifier("entry.key"), + ts.factory.createIdentifier("entry.value"), + ], + ), + entry: props.entry, + required: props.required, + }); + + const decode_entry = (props: { + context: ITypiaContext; + importer: FunctionImporter; + initializer: () => ts.Expression; + setter: () => ts.Expression; + entry: Metadata.Entry; + required: boolean; + }): ts.Statement[] => { + const statements: ts.Statement[] = [ + ...(props.required + ? [] + : [ts.factory.createExpressionStatement(props.initializer())]), + StatementFactory.constant( + "piece", + ts.factory.createAdd( ts.factory.createCallExpression( - IdentifierFactory.access(accessor)("set"), + IdentifierFactory.access(READER())("uint32"), + undefined, undefined, - [ - ts.factory.createIdentifier("entry.key"), - ts.factory.createIdentifier("entry.value"), - ], ), - })(map, required); - - const decode_entry = - (project: ITypiaContext) => - (importer: FunctionImporter) => - (props: { - initializer: () => ts.Expression; - setter: () => ts.Expression; - }) => - (map: Metadata.Entry, required: boolean): ts.Statement[] => { - const statements: ts.Statement[] = [ - ...(required - ? [] - : [ts.factory.createExpressionStatement(props.initializer())]), - StatementFactory.constant( - "piece", - ts.factory.createAdd( - ts.factory.createCallExpression( - IdentifierFactory.access(READER())("uint32"), - undefined, - undefined, - ), - ts.factory.createCallExpression( - IdentifierFactory.access(READER())("index"), - undefined, - undefined, - ), + ts.factory.createCallExpression( + IdentifierFactory.access(READER())("index"), + undefined, + undefined, ), ), - ...write_object_function_body(project)(importer)({ - condition: ts.factory.createLessThan( - ts.factory.createCallExpression( - IdentifierFactory.access(READER())("index"), - undefined, - undefined, - ), - ts.factory.createIdentifier("piece"), + ), + ...write_object_function_body({ + context: props.context, + importer: props.importer, + condition: ts.factory.createLessThan( + ts.factory.createCallExpression( + IdentifierFactory.access(READER())("index"), + undefined, + undefined, ), - tag: "kind", - output: "entry", - })([ + ts.factory.createIdentifier("piece"), + ), + tag: "kind", + output: "entry", + properties: [ MetadataProperty.create({ key: MetadataFactory.soleLiteral("key"), - value: map.key, + value: props.entry.key, description: null, jsDocTags: [], }), MetadataProperty.create({ key: MetadataFactory.soleLiteral("value"), - value: map.value, + value: props.entry.value, description: null, jsDocTags: [], }), - ]), - ts.factory.createExpressionStatement(props.setter()), - ]; - return [ - ts.factory.createExpressionStatement( - ExpressionFactory.selfCall(ts.factory.createBlock(statements, true)), - ), - ]; - }; + ], + }), + ts.factory.createExpressionStatement(props.setter()), + ]; + return [ + ts.factory.createExpressionStatement( + ExpressionFactory.selfCall(ts.factory.createBlock(statements, true)), + ), + ]; + }; } const PREFIX = "$pd"; diff --git a/src/programmers/protobuf/ProtobufEncodeProgrammer.ts b/src/programmers/protobuf/ProtobufEncodeProgrammer.ts index 33de2ee59c..546fbaa45d 100644 --- a/src/programmers/protobuf/ProtobufEncodeProgrammer.ts +++ b/src/programmers/protobuf/ProtobufEncodeProgrammer.ts @@ -36,7 +36,7 @@ export namespace ProtobufEncodeProgrammer { name: string | undefined; }): FeatureProgrammer.IDecomposed => { const collection: MetadataCollection = new MetadataCollection(); - const meta: Metadata = ProtobufFactory.metadata({ + const metadata: Metadata = ProtobufFactory.metadata({ method: props.modulo.getText(), checker: props.context.checker, transformer: props.context.transformer, @@ -44,7 +44,7 @@ export namespace ProtobufEncodeProgrammer { type: props.type, }); - const callEncoder = (writer: string) => (factory: ts.NewExpression) => + const callEncoder = (writer: string, factory: ts.NewExpression) => StatementFactory.constant( writer, ts.factory.createCallExpression( @@ -57,7 +57,12 @@ export namespace ProtobufEncodeProgrammer { functions: { encoder: StatementFactory.constant( props.importer.useLocal("encoder"), - write_encoder(props.context)(props.importer)(collection)(meta), + write_encoder({ + context: props.context, + importer: props.importer, + collection, + metadata, + }), ), }, statements: [], @@ -77,14 +82,16 @@ export namespace ProtobufEncodeProgrammer { undefined, ts.factory.createBlock( [ - callEncoder("sizer")( + callEncoder( + "sizer", ts.factory.createNewExpression( props.importer.use("Sizer"), undefined, [], ), ), - callEncoder("writer")( + callEncoder( + "writer", ts.factory.createNewExpression( props.importer.use("Writer"), undefined, @@ -120,586 +127,683 @@ export namespace ProtobufEncodeProgrammer { }); }; - const write_encoder = - (project: ITypiaContext) => - (importer: FunctionImporter) => - (collection: MetadataCollection) => - (meta: Metadata): ts.ArrowFunction => { - const functors = collection - .objects() - .filter((obj) => ProtobufUtil.isStaticObject(obj)) - .map((obj) => - StatementFactory.constant( - `${PREFIX}o${obj.index}`, - write_object_function(project)(importer)( - ts.factory.createIdentifier("input"), - obj, - { - source: "function", - from: "object", - tracable: false, - postfix: "", - }, - ), - ), - ); - const main = decode(project)(importer)(null)( - ts.factory.createIdentifier("input"), - meta, - { - source: "top", - from: "top", - tracable: false, - postfix: "", - }, + const write_encoder = (props: { + context: ITypiaContext; + importer: FunctionImporter; + collection: MetadataCollection; + metadata: Metadata; + }): ts.ArrowFunction => { + const functors = props.collection + .objects() + .filter((obj) => ProtobufUtil.isStaticObject(obj)) + .map((object) => + StatementFactory.constant( + `${PREFIX}o${object.index}`, + write_object_function({ + context: props.context, + importer: props.importer, + input: ts.factory.createIdentifier("input"), + object, + explore: { + source: "function", + from: "object", + tracable: false, + postfix: "", + }, + }), + ), ); - return ts.factory.createArrowFunction( - undefined, - undefined, + const main: ts.Block = decode({ + context: props.context, + importer: props.importer, + index: null, + input: ts.factory.createIdentifier("input"), + metadata: props.metadata, + explore: { + source: "top", + from: "top", + tracable: false, + postfix: "", + }, + }); + return ts.factory.createArrowFunction( + undefined, + undefined, + [ + IdentifierFactory.parameter("writer"), + IdentifierFactory.parameter("input"), + ], + TypeFactory.keyword("any"), + undefined, + ts.factory.createBlock( [ - IdentifierFactory.parameter("writer"), - IdentifierFactory.parameter("input"), + ...props.importer.declareUnions(), + ...functors, + ...IsProgrammer.write_function_statements(props.context)( + props.importer, + )(props.collection), + ...main.statements, + ts.factory.createReturnStatement( + ts.factory.createIdentifier("writer"), + ), ], - TypeFactory.keyword("any"), - undefined, - ts.factory.createBlock( - [ - ...importer.declareUnions(), - ...functors, - ...IsProgrammer.write_function_statements(project)(importer)( - collection, - ), - ...main.statements, - ts.factory.createReturnStatement( - ts.factory.createIdentifier("writer"), - ), - ], - true, - ), - ); - }; + true, + ), + ); + }; - const write_object_function = - (project: ITypiaContext) => - (importer: FunctionImporter) => - ( - input: ts.Expression, - obj: MetadataObject, - explore: FeatureProgrammer.IExplore, - ): ts.ArrowFunction => { - let index: number = 1; - const body: ts.Statement[] = obj.properties - .map((p) => { - const block = decode(project)(importer)(index)( - IdentifierFactory.access(input)(p.key.getSoleLiteral()!), - p.value, - explore, - ); - index += ProtobufUtil.size(p.value); - return [ - ts.factory.createExpressionStatement( - ts.factory.createIdentifier( - `// property "${p.key.getSoleLiteral()!}"`, - ), + const write_object_function = (props: { + context: ITypiaContext; + importer: FunctionImporter; + input: ts.Expression; + object: MetadataObject; + explore: FeatureProgrammer.IExplore; + }): ts.ArrowFunction => { + let index: number = 1; + const body: ts.Statement[] = props.object.properties + .map((p) => { + const block = decode({ + ...props, + index, + input: IdentifierFactory.access(props.input)(p.key.getSoleLiteral()!), + metadata: p.value, + }); + index += ProtobufUtil.size(p.value); + return [ + ts.factory.createExpressionStatement( + ts.factory.createIdentifier( + `// property "${p.key.getSoleLiteral()!}"`, ), - ...block.statements, - ]; - }) - .flat(); - - return ts.factory.createArrowFunction( - undefined, - undefined, - [IdentifierFactory.parameter("input")], - TypeFactory.keyword("any"), - undefined, - ts.factory.createBlock(body, true), - ); - }; + ), + ...block.statements, + ]; + }) + .flat(); + return ts.factory.createArrowFunction( + undefined, + undefined, + [IdentifierFactory.parameter("input")], + TypeFactory.keyword("any"), + undefined, + ts.factory.createBlock(body, true), + ); + }; /* ----------------------------------------------------------- DECODERS ----------------------------------------------------------- */ - const decode = - (project: ITypiaContext) => - (importer: FunctionImporter) => - (index: number | null) => - ( - input: ts.Expression, - meta: Metadata, - explore: FeatureProgrammer.IExplore, - ): ts.Block => { - const wrapper: (block: ts.Block) => ts.Block = - meta.isRequired() && meta.nullable === false - ? (block) => block - : meta.isRequired() === false && meta.nullable === true + const decode = (props: { + context: ITypiaContext; + importer: FunctionImporter; + index: number | null; + input: ts.Expression; + metadata: Metadata; + explore: FeatureProgrammer.IExplore; + }): ts.Block => { + const wrapper: (block: ts.Block) => ts.Block = + props.metadata.isRequired() && props.metadata.nullable === false + ? (block) => block + : props.metadata.isRequired() === false && + props.metadata.nullable === true + ? (block) => + ts.factory.createBlock( + [ + ts.factory.createIfStatement( + ts.factory.createLogicalAnd( + ts.factory.createStrictInequality( + ts.factory.createIdentifier("undefined"), + props.input, + ), + ts.factory.createStrictInequality( + ts.factory.createNull(), + props.input, + ), + ), + block, + ), + ], + true, + ) + : props.metadata.isRequired() === false ? (block) => ts.factory.createBlock( [ ts.factory.createIfStatement( - ts.factory.createLogicalAnd( - ts.factory.createStrictInequality( - ts.factory.createIdentifier("undefined"), - input, - ), - ts.factory.createStrictInequality( - ts.factory.createNull(), - input, - ), + ts.factory.createStrictInequality( + ts.factory.createIdentifier("undefined"), + props.input, ), block, ), ], true, ) - : meta.isRequired() === false - ? (block) => - ts.factory.createBlock( - [ - ts.factory.createIfStatement( - ts.factory.createStrictInequality( - ts.factory.createIdentifier("undefined"), - input, - ), - block, - ), - ], - true, - ) - : (block) => - ts.factory.createBlock( - [ - ts.factory.createIfStatement( - ts.factory.createStrictInequality( - ts.factory.createNull(), - input, - ), - block, + : (block) => + ts.factory.createBlock( + [ + ts.factory.createIfStatement( + ts.factory.createStrictInequality( + ts.factory.createNull(), + props.input, ), - ], - true, - ); - - // STARTS FROM ATOMIC TYPES - const unions: IUnion[] = []; - const numbers = ProtobufUtil.getNumbers(meta); - const bigints = ProtobufUtil.getBigints(meta); - - for (const atom of ProtobufUtil.getAtomics(meta)) - if (atom === "bool") - unions.push({ - type: "bool", - is: () => - ts.factory.createStrictEquality( - ts.factory.createStringLiteral("boolean"), - ts.factory.createTypeOfExpression(input), - ), - value: (index) => decode_bool(index)(input), - }); - else if ( - atom === "int32" || - atom === "uint32" || - atom === "float" || - atom === "double" - ) - unions.push(decode_number(numbers)(atom)(input)); - else if (atom === "int64" || atom === "uint64") - if (numbers.some((n) => n === atom)) - unions.push(decode_number(numbers)(atom)(input)); - else unions.push(decode_bigint(bigints)(atom)(input)); - else if (atom === "string") - unions.push({ - type: "string", - is: () => - ts.factory.createStrictEquality( - ts.factory.createStringLiteral("string"), - ts.factory.createTypeOfExpression(input), - ), - value: (index) => decode_bytes("string")(index!)(input), - }); + block, + ), + ], + true, + ); - // CONSIDER BYTES - if (meta.natives.length) - unions.push({ - type: "bytes", - is: () => ExpressionFactory.isInstanceOf("Uint8Array")(input), - value: (index) => decode_bytes("bytes")(index!)(input), - }); + // STARTS FROM ATOMIC TYPES + const unions: IUnion[] = []; + const numbers = ProtobufUtil.getNumbers(props.metadata); + const bigints = ProtobufUtil.getBigints(props.metadata); - // CONSIDER ARRAYS - if (meta.arrays.length) + for (const atom of ProtobufUtil.getAtomics(props.metadata)) + if (atom === "bool") unions.push({ - type: "array", - is: () => ExpressionFactory.isArray(input), + type: "bool", + is: () => + ts.factory.createStrictEquality( + ts.factory.createStringLiteral("boolean"), + ts.factory.createTypeOfExpression(props.input), + ), value: (index) => - decode_array(project)(importer)(index!)(input, meta.arrays[0]!, { - ...explore, - from: "array", + decode_bool({ + index, + input: props.input, }), }); - - // CONSIDER MAPS - if (meta.maps.length) - unions.push({ - type: "map", - is: () => ExpressionFactory.isInstanceOf("Map")(input), - value: (index) => - decode_map(project)(importer)(index!)(input, meta.maps[0]!, { - ...explore, - from: "array", + else if ( + atom === "int32" || + atom === "uint32" || + atom === "float" || + atom === "double" + ) + unions.push( + decode_number({ + candidates: numbers, + type: atom, + input: props.input, + }), + ); + else if (atom === "int64" || atom === "uint64") + if (numbers.some((n) => n === atom)) + unions.push( + decode_number({ + candidates: numbers, + type: atom, + input: props.input, }), - }); - - // CONSIDER OBJECTS - if (meta.objects.length) + ); + else + unions.push( + decode_bigint({ + candidates: bigints, + type: atom, + input: props.input, + }), + ); + else if (atom === "string") unions.push({ - type: "object", + type: "string", is: () => - ExpressionFactory.isObject({ - checkNull: true, - checkArray: false, - })(input), + ts.factory.createStrictEquality( + ts.factory.createStringLiteral("string"), + ts.factory.createTypeOfExpression(props.input), + ), value: (index) => - explore_objects(project)(importer)(0)(index)(input, meta.objects, { - ...explore, - from: "object", + decode_bytes({ + method: "string", + index: index!, + input: props.input, }), }); - // RETURNS - if (unions.length === 1) return wrapper(unions[0]!.value(index)); - else - return wrapper(iterate(importer)(index)(unions)(meta.getName())(input)); - }; - - const iterate = - (importer: FunctionImporter) => - (index: number | null) => - (unions: IUnion[]) => - (expected: string) => - (input: ts.Expression) => - ts.factory.createBlock( - [ - unions - .map((u, i) => - ts.factory.createIfStatement( - u.is(), - u.value(index ? index + i : null), - i === unions.length - 1 - ? create_throw_error(importer)(expected)(input) - : undefined, - ), - ) - .reverse() - .reduce((a, b) => - ts.factory.createIfStatement(b.expression, b.thenStatement, a), - ), - ], - true, - ); - - const decode_map = - (project: ITypiaContext) => - (importer: FunctionImporter) => - (index: number) => - ( - input: ts.Expression, - map: Metadata.Entry, - explore: FeatureProgrammer.IExplore, - ): ts.Block => { - const each: ts.Statement[] = [ - ts.factory.createExpressionStatement( - decode_tag(ProtobufWire.LEN)(index), - ), - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - IdentifierFactory.access(WRITER())("fork"), - undefined, - undefined, - ), - ), - ...decode(project)(importer)(1)( - ts.factory.createIdentifier("key"), - map.key, - explore, - ).statements, - ...decode(project)(importer)(2)( - ts.factory.createIdentifier("value"), - map.value, - explore, - ).statements, - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - IdentifierFactory.access(WRITER())("ldelim"), - undefined, - undefined, - ), - ), - ]; - return ts.factory.createBlock( - [ - ts.factory.createForOfStatement( - undefined, - StatementFactory.entry("key")("value"), - input, - ts.factory.createBlock(each), - ), - ], - true, + // CONSIDER BYTES + if (props.metadata.natives.length) + unions.push({ + type: "bytes", + is: () => ExpressionFactory.isInstanceOf("Uint8Array")(props.input), + value: (index) => + decode_bytes({ + method: "bytes", + index: index!, + input: props.input, + }), + }); + + // CONSIDER ARRAYS + if (props.metadata.arrays.length) + unions.push({ + type: "array", + is: () => ExpressionFactory.isArray(props.input), + value: (index) => + decode_array({ + ...props, + array: props.metadata.arrays[0]!, + explore: { + ...props.explore, + from: "array", + }, + index: index!, + }), + }); + + // CONSIDER MAPS + if (props.metadata.maps.length) + unions.push({ + type: "map", + is: () => ExpressionFactory.isInstanceOf("Map")(props.input), + value: (index) => + decode_map({ + ...props, + index: index!, + entry: props.metadata.maps[0]!, + explore: { + ...props.explore, + from: "array", + }, + }), + }); + + // CONSIDER OBJECTS + if (props.metadata.objects.length) + unions.push({ + type: "object", + is: () => + ExpressionFactory.isObject({ + checkNull: true, + checkArray: false, + })(props.input), + value: (index) => + explore_objects({ + ...props, + level: 0, + index, + objects: props.metadata.objects, + explore: { + ...props.explore, + from: "object", + }, + }), + }); + + // RETURNS + if (unions.length === 1) return wrapper(unions[0]!.value(props.index)); + else + return wrapper( + iterate({ + importer: props.importer, + index: props.index, + unions, + expected: props.metadata.getName(), + input: props.input, + }), ); - }; + }; - const decode_object = - (project: ITypiaContext) => - (importer: FunctionImporter) => - (index: number | null) => - ( - input: ts.Expression, - object: MetadataObject, - explore: FeatureProgrammer.IExplore, - ): ts.Block => { - const top: MetadataProperty = object.properties[0]!; - if (top.key.isSoleLiteral() === false) - return decode_map(project)(importer)(index!)( - ts.factory.createCallExpression( - ts.factory.createIdentifier("Object.entries"), - [], - [input], - ), - MetadataProperty.create({ - ...top, - key: (() => { - const key: Metadata = Metadata.initialize(); - key.atomics.push( - MetadataAtomic.create({ - type: "string", - tags: [], - }), - ); - return key; - })(), - }), - explore, - ); - return ts.factory.createBlock( - [ - ts.factory.createIdentifier( - `//${index !== null ? ` ${index} -> ` : ""}${object.name}`, - ), - ...(index !== null - ? [ - decode_tag(ProtobufWire.LEN)(index), - ts.factory.createCallExpression( - IdentifierFactory.access(WRITER())("fork"), - undefined, - undefined, - ), - ] - : []), - ts.factory.createCallExpression( - ts.factory.createIdentifier( - importer.useLocal(`${PREFIX}o${object.index}`), + const iterate = (props: { + importer: FunctionImporter; + index: number | null; + unions: IUnion[]; + expected: string; + input: ts.Expression; + }) => + ts.factory.createBlock( + [ + props.unions + .map((u, i) => + ts.factory.createIfStatement( + u.is(), + u.value(props.index ? props.index + i : null), + i === props.unions.length - 1 + ? create_throw_error(props) + : undefined, ), - [], - [input], + ) + .reverse() + .reduce((a, b) => + ts.factory.createIfStatement(b.expression, b.thenStatement, a), ), - ...(index !== null - ? [ - ts.factory.createCallExpression( - IdentifierFactory.access(WRITER())("ldelim"), - undefined, - undefined, - ), - ] - : []), - ].map((expr) => ts.factory.createExpressionStatement(expr)), - true, - ); - }; + ], + true, + ); - const decode_array = - (project: ITypiaContext) => - (importer: FunctionImporter) => - (index: number) => - ( - input: ts.Expression, - array: MetadataArray, - explore: FeatureProgrammer.IExplore, - ): ts.Block => { - const wire = get_standalone_wire(array.type.value); - const forLoop = (index: number | null) => + const decode_map = (props: { + context: ITypiaContext; + importer: FunctionImporter; + index: number; + input: ts.Expression; + entry: Metadata.Entry; + explore: FeatureProgrammer.IExplore; + }): ts.Block => { + const each: ts.Statement[] = [ + ts.factory.createExpressionStatement( + decode_tag({ + wire: ProtobufWire.LEN, + index: props.index, + }), + ), + ts.factory.createExpressionStatement( + ts.factory.createCallExpression( + IdentifierFactory.access(WRITER())("fork"), + undefined, + undefined, + ), + ), + ...decode({ + ...props, + index: 1, + input: ts.factory.createIdentifier("key"), + metadata: props.entry.key, + }).statements, + ...decode({ + ...props, + index: 2, + input: ts.factory.createIdentifier("value"), + metadata: props.entry.value, + }).statements, + ts.factory.createExpressionStatement( + ts.factory.createCallExpression( + IdentifierFactory.access(WRITER())("ldelim"), + undefined, + undefined, + ), + ), + ]; + return ts.factory.createBlock( + [ ts.factory.createForOfStatement( undefined, - ts.factory.createVariableDeclarationList( - [ts.factory.createVariableDeclaration("elem")], - ts.NodeFlags.Const, - ), - input, - decode(project)(importer)(index)( - ts.factory.createIdentifier("elem"), - array.type.value, - explore, - ), - ); - const length = (block: ts.Block) => - ts.factory.createBlock( - [ - ts.factory.createIfStatement( - ts.factory.createStrictInequality( - ExpressionFactory.number(0), - IdentifierFactory.access(input)("length"), - ), - block, - ), - ], - true, - ); + StatementFactory.entry("key")("value"), + props.input, + ts.factory.createBlock(each), + ), + ], + true, + ); + }; - if (wire === ProtobufWire.LEN) - return length(ts.factory.createBlock([forLoop(index)], true)); - return length( - ts.factory.createBlock( - [ - ts.factory.createExpressionStatement( - decode_tag(ProtobufWire.LEN)(index), - ), - ts.factory.createExpressionStatement( + const decode_object = (props: { + context: ITypiaContext; + importer: FunctionImporter; + index: number | null; + input: ts.Expression; + object: MetadataObject; + explore: FeatureProgrammer.IExplore; + }): ts.Block => { + const top: MetadataProperty = props.object.properties[0]!; + if (top.key.isSoleLiteral() === false) + return decode_map({ + ...props, + index: props.index!, + input: ts.factory.createCallExpression( + ts.factory.createIdentifier("Object.entries"), + [], + [props.input], + ), + entry: MetadataProperty.create({ + ...top, + key: (() => { + const key: Metadata = Metadata.initialize(); + key.atomics.push( + MetadataAtomic.create({ + type: "string", + tags: [], + }), + ); + return key; + })(), + }), + }); + return ts.factory.createBlock( + [ + ts.factory.createIdentifier( + `//${props.index !== null ? ` ${props.index} -> ` : ""}${props.object.name}`, + ), + ...(props.index !== null + ? [ + decode_tag({ + wire: ProtobufWire.LEN, + index: props.index, + }), ts.factory.createCallExpression( IdentifierFactory.access(WRITER())("fork"), undefined, undefined, ), - ), - forLoop(null), - ts.factory.createExpressionStatement( + ] + : []), + ts.factory.createCallExpression( + ts.factory.createIdentifier( + props.importer.useLocal(`${PREFIX}o${props.object.index}`), + ), + [], + [props.input], + ), + ...(props.index !== null + ? [ ts.factory.createCallExpression( IdentifierFactory.access(WRITER())("ldelim"), undefined, undefined, ), - ), - ], - true, + ] + : []), + ].map((expr) => ts.factory.createExpressionStatement(expr)), + true, + ); + }; + + const decode_array = (props: { + context: ITypiaContext; + importer: FunctionImporter; + index: number; + input: ts.Expression; + array: MetadataArray; + explore: FeatureProgrammer.IExplore; + }): ts.Block => { + const wire = get_standalone_wire(props.array.type.value); + const forLoop = (index: number | null) => + ts.factory.createForOfStatement( + undefined, + ts.factory.createVariableDeclarationList( + [ts.factory.createVariableDeclaration("elem")], + ts.NodeFlags.Const, ), + props.input, + decode({ + ...props, + input: ts.factory.createIdentifier("elem"), + index, + metadata: props.array.type.value, + }), + ); + const length = (block: ts.Block) => + ts.factory.createBlock( + [ + ts.factory.createIfStatement( + ts.factory.createStrictInequality( + ExpressionFactory.number(0), + IdentifierFactory.access(props.input)("length"), + ), + block, + ), + ], + true, ); - }; - const decode_bool = (index: number | null) => (input: ts.Expression) => + if (wire === ProtobufWire.LEN) + return length(ts.factory.createBlock([forLoop(props.index)], true)); + return length( + ts.factory.createBlock( + [ + ts.factory.createExpressionStatement( + decode_tag({ + wire: ProtobufWire.LEN, + index: props.index, + }), + ), + ts.factory.createExpressionStatement( + ts.factory.createCallExpression( + IdentifierFactory.access(WRITER())("fork"), + undefined, + undefined, + ), + ), + forLoop(null), + ts.factory.createExpressionStatement( + ts.factory.createCallExpression( + IdentifierFactory.access(WRITER())("ldelim"), + undefined, + undefined, + ), + ), + ], + true, + ), + ); + }; + + const decode_bool = (props: { index: number | null; input: ts.Expression }) => ts.factory.createBlock( [ - ...(index !== null ? [decode_tag(ProtobufWire.VARIANT)(index)] : []), + ...(props.index !== null + ? [ + decode_tag({ + wire: ProtobufWire.VARIANT, + index: props.index, + }), + ] + : []), ts.factory.createCallExpression( IdentifierFactory.access(WRITER())("bool"), undefined, - [input], + [props.input], ), ].map((exp) => ts.factory.createExpressionStatement(exp)), true, ); - const decode_number = - (candidates: ProtobufAtomic.Numeric[]) => - (type: ProtobufAtomic.Numeric) => - (input: ts.Expression): IUnion => ({ - type, - is: () => - candidates.length === 1 - ? ts.factory.createStrictEquality( + const decode_number = (props: { + candidates: ProtobufAtomic.Numeric[]; + type: ProtobufAtomic.Numeric; + input: ts.Expression; + }): IUnion => ({ + type: props.type, + is: () => + props.candidates.length === 1 + ? ts.factory.createStrictEquality( + ts.factory.createStringLiteral("number"), + ts.factory.createTypeOfExpression(props.input), + ) + : ts.factory.createLogicalAnd( + ts.factory.createStrictEquality( ts.factory.createStringLiteral("number"), - ts.factory.createTypeOfExpression(input), - ) - : ts.factory.createLogicalAnd( - ts.factory.createStrictEquality( - ts.factory.createStringLiteral("number"), - ts.factory.createTypeOfExpression(input), - ), - NumericRangeFactory.number(type)(input), - ), - value: (index) => - ts.factory.createBlock( - [ - ...(index !== null - ? [decode_tag(get_numeric_wire(type))(index)] - : []), - ts.factory.createCallExpression( - IdentifierFactory.access(WRITER())(type), - undefined, - [input], + ts.factory.createTypeOfExpression(props.input), ), - ].map((exp) => ts.factory.createExpressionStatement(exp)), - true, - ), - }); - - const decode_bigint = - (candidates: ProtobufAtomic.BigNumeric[]) => - (type: ProtobufAtomic.BigNumeric) => - (input: ts.Expression): IUnion => ({ - type, - is: () => - candidates.length === 1 - ? ts.factory.createStrictEquality( + NumericRangeFactory.number(props.type)(props.input), + ), + value: (index) => + ts.factory.createBlock( + [ + ...(index !== null + ? [ + decode_tag({ + wire: get_numeric_wire(props.type), + index, + }), + ] + : []), + ts.factory.createCallExpression( + IdentifierFactory.access(WRITER())(props.type), + undefined, + [props.input], + ), + ].map((exp) => ts.factory.createExpressionStatement(exp)), + true, + ), + }); + + const decode_bigint = (props: { + candidates: ProtobufAtomic.BigNumeric[]; + type: ProtobufAtomic.BigNumeric; + input: ts.Expression; + }): IUnion => ({ + type: props.type, + is: () => + props.candidates.length === 1 + ? ts.factory.createStrictEquality( + ts.factory.createStringLiteral("bigint"), + ts.factory.createTypeOfExpression(props.input), + ) + : ts.factory.createLogicalAnd( + ts.factory.createStrictEquality( ts.factory.createStringLiteral("bigint"), - ts.factory.createTypeOfExpression(input), - ) - : ts.factory.createLogicalAnd( - ts.factory.createStrictEquality( - ts.factory.createStringLiteral("bigint"), - ts.factory.createTypeOfExpression(input), - ), - NumericRangeFactory.bigint(type)(input), - ), - value: (index) => - ts.factory.createBlock( - [ - ...(index !== null - ? [decode_tag(ProtobufWire.VARIANT)(index)] - : []), - ts.factory.createCallExpression( - IdentifierFactory.access(WRITER())(type), - undefined, - [input], + ts.factory.createTypeOfExpression(props.input), ), - ].map((exp) => ts.factory.createExpressionStatement(exp)), - true, - ), - }); - - const decode_bytes = - (method: "bytes" | "string") => - (index: number) => - (input: ts.Expression): ts.Block => + NumericRangeFactory.bigint(props.type)(props.input), + ), + value: (index) => ts.factory.createBlock( [ - decode_tag(ProtobufWire.LEN)(index), + ...(index !== null + ? [ + decode_tag({ + wire: ProtobufWire.VARIANT, + index, + }), + ] + : []), ts.factory.createCallExpression( - IdentifierFactory.access(WRITER())(method), + IdentifierFactory.access(WRITER())(props.type), undefined, - [input], + [props.input], ), - ].map((expr) => ts.factory.createExpressionStatement(expr)), + ].map((exp) => ts.factory.createExpressionStatement(exp)), true, - ); + ), + }); - const decode_tag = - (wire: ProtobufWire) => - (index: number): ts.CallExpression => - ts.factory.createCallExpression( - IdentifierFactory.access(WRITER())("uint32"), - undefined, - [ExpressionFactory.number((index << 3) | wire)], - ); + const decode_bytes = (props: { + method: "bytes" | "string"; + index: number; + input: ts.Expression; + }): ts.Block => + ts.factory.createBlock( + [ + decode_tag({ + wire: ProtobufWire.LEN, + index: props.index, + }), + ts.factory.createCallExpression( + IdentifierFactory.access(WRITER())(props.method), + undefined, + [props.input], + ), + ].map((expr) => ts.factory.createExpressionStatement(expr)), + true, + ); - const get_standalone_wire = (meta: Metadata): ProtobufWire => { + const decode_tag = (props: { + wire: ProtobufWire; + index: number; + }): ts.CallExpression => + ts.factory.createCallExpression( + IdentifierFactory.access(WRITER())("uint32"), + undefined, + [ExpressionFactory.number((props.index << 3) | props.wire)], + ); + + const get_standalone_wire = (metadata: Metadata): ProtobufWire => { if ( - meta.arrays.length || - meta.objects.length || - meta.maps.length || - meta.natives.length + metadata.arrays.length || + metadata.objects.length || + metadata.maps.length || + metadata.natives.length ) return ProtobufWire.LEN; - const v = ProtobufUtil.getAtomics(meta)[0]!; + const v = ProtobufUtil.getAtomics(metadata)[0]!; if (v === "string") return ProtobufWire.LEN; else if ( v === "bool" || @@ -723,124 +827,159 @@ export namespace ProtobufEncodeProgrammer { /* ----------------------------------------------------------- EXPLORERS ----------------------------------------------------------- */ - const explore_objects = - (project: ITypiaContext) => - (importer: FunctionImporter) => - (level: number) => - (index: number | null) => - ( - input: ts.Expression, - targets: MetadataObject[], - explore: FeatureProgrammer.IExplore, - indexes?: Map, - ): ts.Block => { - if (targets.length === 1) - return decode_object(project)(importer)( - indexes ? indexes.get(targets[0]!)! : index, - )(input, targets[0]!, explore); - - const expected: string = `(${targets.map((t) => t.name).join(" | ")})`; - - // POSSIBLE TO SPECIALIZE? - const specList = UnionPredicator.object(targets); - indexes ??= new Map(targets.map((t, i) => [t, index! + i])); - - if (specList.length === 0) { - const condition: ts.Expression = decode_union_object( - IsProgrammer.decode_object(project)(importer), - )((i, o, e) => - ExpressionFactory.selfCall( - decode_object(project)(importer)(indexes!.get(o)!)(i, o, e), - ), - )((expr) => expr)((value, expected) => - create_throw_error(importer)(expected)(value), - )(input, targets, explore); - return StatementFactory.block(condition); - } - const remained: MetadataObject[] = targets.filter( - (t) => specList.find((s) => s.object === t) === undefined, - ); + const explore_objects = (props: { + context: ITypiaContext; + importer: FunctionImporter; + level: number; + index: number | null; + input: ts.Expression; + objects: MetadataObject[]; + explore: FeatureProgrammer.IExplore; + indexes?: Map; + }): ts.Block => { + if (props.objects.length === 1) + return decode_object({ + context: props.context, + importer: props.importer, + index: props.indexes + ? props.indexes.get(props.objects[0]!)! + : props.index, + input: props.input, + object: props.objects[0]!, + explore: props.explore, + }); + + const expected: string = `(${props.objects.map((t) => t.name).join(" | ")})`; + + // POSSIBLE TO SPECIALIZE? + const specList: UnionPredicator.ISpecialized[] = UnionPredicator.object( + props.objects, + ); + const indexes: Map = + props.indexes ?? + new Map(props.objects.map((t, i) => [t, props.index! + i])); + + if (specList.length === 0) { + const condition: ts.Expression = decode_union_object( + IsProgrammer.decode_object(props.context)(props.importer), + )((input, object, explore) => + ExpressionFactory.selfCall( + decode_object({ + context: props.context, + importer: props.importer, + index: indexes.get(object)!, + input, + object, + explore, + }), + ), + )((expr) => expr)((input, expected) => + create_throw_error({ + importer: props.importer, + expected, + input, + }), + )(props.input, props.objects, props.explore); + return StatementFactory.block(condition); + } + const remained: MetadataObject[] = props.objects.filter( + (t) => specList.find((s) => s.object === t) === undefined, + ); - // DO SPECIALIZE - const condition: ts.IfStatement = specList - .filter((spec) => spec.property.key.getSoleLiteral() !== null) - .map((spec, i, array) => { - const key: string = spec.property.key.getSoleLiteral()!; - const accessor: ts.Expression = IdentifierFactory.access(input)(key); - const pred: ts.Expression = spec.neighbour - ? IsProgrammer.decode(project)(importer)( - accessor, - spec.property.value, - { - ...explore, - tracable: false, - postfix: IdentifierFactory.postfix(key), - }, - ) - : ExpressionFactory.isRequired(accessor); - return ts.factory.createIfStatement( - pred, - ts.factory.createExpressionStatement( - ExpressionFactory.selfCall( - decode_object(project)(importer)(indexes!.get(spec.object)!)( - input, - spec.object, - explore, - ), - ), + // DO SPECIALIZE + const condition: ts.IfStatement = specList + .filter((spec) => spec.property.key.getSoleLiteral() !== null) + .map((spec, i, array) => { + const key: string = spec.property.key.getSoleLiteral()!; + const accessor: ts.Expression = IdentifierFactory.access(props.input)( + key, + ); + const pred: ts.Expression = spec.neighbour + ? IsProgrammer.decode(props.context)(props.importer)( + accessor, + spec.property.value, + { + ...props.explore, + tracable: false, + postfix: IdentifierFactory.postfix(key), + }, + ) + : ExpressionFactory.isRequired(accessor); + return ts.factory.createIfStatement( + pred, + ts.factory.createExpressionStatement( + ExpressionFactory.selfCall( + decode_object({ + context: props.context, + importer: props.importer, + index: indexes.get(spec.object)!, + input: props.input, + object: spec.object, + explore: props.explore, + }), ), - i === array.length - 1 - ? remained.length - ? ts.factory.createExpressionStatement( - ExpressionFactory.selfCall( - explore_objects(project)(importer)(level + 1)(index)( - input, - remained, - explore, - indexes!, - ), - ), - ) - : create_throw_error(importer)(expected)(input) - : undefined, - ); - }) - .reverse() - .reduce((a, b) => - ts.factory.createIfStatement(b.expression, b.thenStatement, a), + ), + i === array.length - 1 + ? remained.length + ? ts.factory.createExpressionStatement( + ExpressionFactory.selfCall( + explore_objects({ + context: props.context, + importer: props.importer, + level: props.level + 1, + index: props.index, + input: props.input, + objects: remained, + explore: props.explore, + indexes, + }), + ), + ) + : create_throw_error({ + importer: props.importer, + input: props.input, + expected, + }) + : undefined, ); + }) + .reverse() + .reduce((a, b) => + ts.factory.createIfStatement(b.expression, b.thenStatement, a), + ); - // RETURNS WITH CONDITIONS - return ts.factory.createBlock([condition], true); - }; + // RETURNS WITH CONDITIONS + return ts.factory.createBlock([condition], true); + }; /* ----------------------------------------------------------- CONFIGURATIONS ----------------------------------------------------------- */ const PREFIX = "$pe"; - const create_throw_error = - (importer: FunctionImporter) => - (expected: string) => - (value: ts.Expression) => - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - importer.use("throws"), - [], - [ - ts.factory.createObjectLiteralExpression( - [ - ts.factory.createPropertyAssignment( - "expected", - ts.factory.createStringLiteral(expected), - ), - ts.factory.createPropertyAssignment("value", value), - ], - true, - ), - ], - ), - ); + const create_throw_error = (props: { + importer: FunctionImporter; + expected: string; + input: ts.Expression; + }) => + ts.factory.createExpressionStatement( + ts.factory.createCallExpression( + props.importer.use("throws"), + [], + [ + ts.factory.createObjectLiteralExpression( + [ + ts.factory.createPropertyAssignment( + "expected", + ts.factory.createStringLiteral(props.expected), + ), + ts.factory.createPropertyAssignment("value", props.input), + ], + true, + ), + ], + ), + ); } const WRITER = () => ts.factory.createIdentifier("writer"); diff --git a/src/programmers/protobuf/ProtobufMessageProgrammer.ts b/src/programmers/protobuf/ProtobufMessageProgrammer.ts index 53cd5d2e1c..a761676b70 100644 --- a/src/programmers/protobuf/ProtobufMessageProgrammer.ts +++ b/src/programmers/protobuf/ProtobufMessageProgrammer.ts @@ -33,8 +33,12 @@ export namespace ProtobufMessageProgrammer { // STRINGIFY const hierarchies: Map = new Map(); - for (const obj of collection.objects()) - if (is_dynamic_object(obj) === false) emplace(hierarchies)(obj); + for (const object of collection.objects()) + if (is_dynamic_object(object) === false) + emplace({ + hierarchies, + object, + }); const content: string = `syntax = "proto3";\n\n` + @@ -46,22 +50,26 @@ export namespace ProtobufMessageProgrammer { return ts.factory.createStringLiteral(content); }; - const emplace = (dict: Map) => (obj: MetadataObject) => { - const accessors: string[] = obj.name.split("."); + const emplace = (props: { + hierarchies: Map; + object: MetadataObject; + }) => { + let hierarchies: Map = props.hierarchies; + const accessors: string[] = props.object.name.split("."); accessors.forEach((access, i) => { - const hierarchy: Hierarchy = MapUtil.take(dict)(access, () => ({ + const hierarchy: Hierarchy = MapUtil.take(hierarchies)(access, () => ({ key: access, object: null!, children: new Map(), })); - dict = hierarchy.children; - if (i === accessors.length - 1) hierarchy.object = obj; + hierarchies = hierarchy.children; + if (i === accessors.length - 1) hierarchy.object = props.object; }); }; - const is_dynamic_object = (obj: MetadataObject): boolean => - obj.properties.length === 1 && - obj.properties[0]!.key.isSoleLiteral() === false; + const is_dynamic_object = (object: MetadataObject): boolean => + object.properties.length === 1 && + object.properties[0]!.key.isSoleLiteral() === false; const write_hierarchy = (hierarchy: Hierarchy): string => { const elements: string[] = [ @@ -87,21 +95,24 @@ export namespace ProtobufMessageProgrammer { return elements.join("\n"); }; - const write_object = (obj: MetadataObject): string => { - const ptr: IPointer = { value: 0 }; - return obj.properties - .map((prop) => { - const key: string = prop.key.getSoleLiteral()!; - const type: string = decode(ptr)(prop.value); + const write_object = (object: MetadataObject): string => { + const sequence: IPointer = { value: 0 }; + return object.properties + .map((p) => { + const key: string = p.key.getSoleLiteral()!; + const type: string = decode({ + sequence, + metadata: p.value, + }); return type.indexOf("${name}") !== -1 ? type.replace("${name}", key) : `${ - prop.value.arrays.length || type.startsWith("map<") + p.value.arrays.length || type.startsWith("map<") ? "" - : !prop.value.isRequired() || prop.value.nullable + : !p.value.isRequired() || p.value.nullable ? "optional " : "required " - }${type} ${key} = ${++ptr.value};`; + }${type} ${key} = ${++sequence.value};`; }) .join("\n"); }; @@ -109,50 +120,72 @@ export namespace ProtobufMessageProgrammer { /* ----------------------------------------------------------- DECODERS ----------------------------------------------------------- */ - const decode = - (ptr: IPointer) => - (meta: Metadata): string => { - const elements: Set = new Set(); - if (meta.natives.length) elements.add("bytes"); - for (const atomic of ProtobufUtil.getAtomics(meta)) elements.add(atomic); - for (const array of meta.arrays) - elements.add(`repeated ${decode(ptr)(array.type.value)}`); - for (const obj of meta.objects) - elements.add( - is_dynamic_object(obj) - ? decode_map(ptr)( - MetadataProperty.create({ - ...obj.properties[0]!, - key: (() => { - const key: Metadata = Metadata.initialize(); - key.atomics.push( - MetadataAtomic.create({ - type: "string", - tags: [], - }), - ); - return key; - })(), - }), - ) - : NameEncoder.encode(obj.name), - ); - for (const map of meta.maps) elements.add(decode_map(ptr)(map)); - return elements.size === 1 - ? [...elements][0]! - : [ - "oneof ${name} {", - ...[...elements].map( - (str) => `${TAB}${str} v${ptr.value + 1} = ${++ptr.value};`, - ), - "}", - ].join("\n"); - }; - - const decode_map = - (ptr: IPointer) => - (prop: Metadata.Entry): string => - `map<${decode(ptr)(prop.key)}, ${decode(ptr)(prop.value)}>`; + const decode = (props: { + sequence: IPointer; + metadata: Metadata; + }): string => { + const elements: Set = new Set(); + if (props.metadata.natives.length) elements.add("bytes"); + for (const atomic of ProtobufUtil.getAtomics(props.metadata)) + elements.add(atomic); + for (const array of props.metadata.arrays) + elements.add( + `repeated ${decode({ + sequence: props.sequence, + metadata: array.type.value, + })}`, + ); + for (const obj of props.metadata.objects) + elements.add( + is_dynamic_object(obj) + ? decode_map({ + sequence: props.sequence, + entry: MetadataProperty.create({ + ...obj.properties[0]!, + key: (() => { + const key: Metadata = Metadata.initialize(); + key.atomics.push( + MetadataAtomic.create({ + type: "string", + tags: [], + }), + ); + return key; + })(), + }), + }) + : NameEncoder.encode(obj.name), + ); + for (const entry of props.metadata.maps) + elements.add( + decode_map({ + sequence: props.sequence, + entry, + }), + ); + return elements.size === 1 + ? [...elements][0]! + : [ + "oneof ${name} {", + ...[...elements].map((str) => { + const index: number = ++props.sequence.value; + return `${TAB}${str} v${index} = ${index};`; + }), + "}", + ].join("\n"); + }; + + const decode_map = (props: { + sequence: IPointer; + entry: Metadata.Entry; + }): string => + `map<${decode({ + sequence: props.sequence, + metadata: props.entry.key, + })}, ${decode({ + sequence: props.sequence, + metadata: props.entry.value, + })}>`; } interface Hierarchy { diff --git a/test-esm/package.json b/test-esm/package.json index 4701625bba..2a1afe502d 100644 --- a/test-esm/package.json +++ b/test-esm/package.json @@ -36,6 +36,6 @@ "typescript": "^5.4.5" }, "dependencies": { - "typia": "../typia-7.0.0-dev.20240921.tgz" + "typia": "../typia-7.0.0-dev.20240922.tgz" } } \ No newline at end of file diff --git a/test/package.json b/test/package.json index e3fc898028..476231277b 100644 --- a/test/package.json +++ b/test/package.json @@ -52,6 +52,6 @@ "suppress-warnings": "^1.0.2", "tstl": "^3.0.0", "uuid": "^9.0.1", - "typia": "../typia-7.0.0-dev.20240921.tgz" + "typia": "../typia-7.0.0-dev.20240922.tgz" } } \ No newline at end of file