UNPKG

typia

Version:

Superfast runtime validators with only one line

161 lines (142 loc) • 5.25 kB
import ts from "typescript"; import { MetadataCollection } from "../../factories/MetadataCollection"; import { ProtobufFactory } from "../../factories/ProtobufFactory"; import { Metadata } from "../../schemas/metadata/Metadata"; import { MetadataAtomic } from "../../schemas/metadata/MetadataAtomic"; import { MetadataObject } from "../../schemas/metadata/MetadataObject"; import { MetadataProperty } from "../../schemas/metadata/MetadataProperty"; import { IProject } from "../../transformers/IProject"; import { MapUtil } from "../../utils/MapUtil"; import { NameEncoder } from "../../utils/NameEncoder"; import { ProtobufUtil } from "../helpers/ProtobufUtil"; export namespace ProtobufMessageProgrammer { export const write = (project: IProject) => (type: ts.Type) => { // PARSE TARGET TYPE const collection: MetadataCollection = new MetadataCollection(); ProtobufFactory.metadata("message")(project.checker, project.context)( collection, )(type); // STRINGIFY const hierarchies: Map<string, Hierarchy> = new Map(); for (const obj of collection.objects()) if (is_dynamic_object(obj) === false) emplace(hierarchies)(obj); const content: string = `syntax = "proto3";\n\n` + [...hierarchies.values()] .map((hier) => write_hierarchy(hier)) .join("\n\n"); // RETURNS return ts.factory.createStringLiteral(content); }; const emplace = (dict: Map<string, Hierarchy>) => (obj: MetadataObject) => { const accessors: string[] = obj.name.split("."); accessors.forEach((access, i) => { const hierarchy: Hierarchy = MapUtil.take(dict)(access, () => ({ key: access, object: null!, children: new Map(), })); dict = hierarchy.children; if (i === accessors.length - 1) hierarchy.object = obj; }); }; const is_dynamic_object = (obj: MetadataObject): boolean => obj.properties.length === 1 && obj.properties[0]!.key.isSoleLiteral() === false; const write_hierarchy = (hierarchy: Hierarchy): string => { const elements: string[] = [ `message ${NameEncoder.encode(hierarchy.key)} {`, ]; if (hierarchy.object !== null) { const text: string = write_object(hierarchy.object); elements.push(...text.split("\n").map((str) => `${TAB}${str}`)); } if (hierarchy.children.size) elements.push( [...hierarchy.children.values()] .map((child) => write_hierarchy(child)) .map((body) => body .split("\n") .map((line) => `${TAB}${line}`) .join("\n"), ) .join("\n\n"), ); elements.push("}"); return elements.join("\n"); }; const write_object = (obj: MetadataObject): string => { const ptr: IPointer<number> = { value: 0 }; return obj.properties .map((prop) => { const key: string = prop.key.getSoleLiteral()!; const type: string = decode(ptr)(prop.value); return type.indexOf("${name}") !== -1 ? type.replace("${name}", key) : `${ prop.value.arrays.length || type.startsWith("map<") ? "" : !prop.value.isRequired() || prop.value.nullable ? "optional " : "required " }${type} ${key} = ${++ptr.value};`; }) .join("\n"); }; /* ----------------------------------------------------------- DECODERS ----------------------------------------------------------- */ const decode = (ptr: IPointer<number>) => (meta: Metadata): string => { const elements: Set<string> = 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<number>) => (prop: Metadata.Entry): string => `map<${decode(ptr)(prop.key)}, ${decode(ptr)(prop.value)}>`; } interface Hierarchy { key: string; object: MetadataObject | null; children: Map<string, Hierarchy>; } interface IPointer<T> { value: T; } const TAB = " ".repeat(2);