UNPKG

typia

Version:

Superfast runtime validators with only one line

398 lines (385 loc) • 13.9 kB
import ts from "typescript"; import { IdentifierFactory } from "../factories/IdentifierFactory"; import { StatementFactory } from "../factories/StatementFactory"; import { TypeFactory } from "../factories/TypeFactory"; import { IProject } from "../transformers/IProject"; import { CheckerProgrammer } from "./CheckerProgrammer"; import { FeatureProgrammer } from "./FeatureProgrammer"; import { IsProgrammer } from "./IsProgrammer"; import { FunctionImporter } from "./helpers/FunctionImporter"; import { OptionPredicator } from "./helpers/OptionPredicator"; import { check_object } from "./internal/check_object"; export namespace AssertProgrammer { export const decompose = (props: { project: IProject; equals: boolean; guard: boolean; importer: FunctionImporter; type: ts.Type; name: string | undefined; init: ts.Expression | undefined; }): FeatureProgrammer.IDecomposed => { const is: FeatureProgrammer.IDecomposed = IsProgrammer.decompose(props); const composed: FeatureProgrammer.IComposed = CheckerProgrammer.compose({ ...props, config: { prefix: "$a", path: true, trace: true, numeric: OptionPredicator.numeric(props.project.options), equals: props.equals, atomist: (explore) => (entry) => (input) => [ ...(entry.expression ? [entry.expression] : []), ...(entry.conditions.length === 0 ? [] : entry.conditions.length === 1 ? entry.conditions[0]!.map((cond) => ts.factory.createLogicalOr( cond.expression, create_guard_call(props.importer)( explore.from === "top" ? ts.factory.createTrue() : ts.factory.createIdentifier("_exceptionable"), )( ts.factory.createIdentifier( explore.postfix ? `_path + ${explore.postfix}` : "_path", ), cond.expected, input, ), ), ) : [ ts.factory.createLogicalOr( entry.conditions .map((set) => set .map((s) => s.expression) .reduce((a, b) => ts.factory.createLogicalAnd(a, b), ), ) .reduce((a, b) => ts.factory.createLogicalOr(a, b)), create_guard_call(props.importer)( explore.from === "top" ? ts.factory.createTrue() : ts.factory.createIdentifier("_exceptionable"), )( ts.factory.createIdentifier( explore.postfix ? `_path + ${explore.postfix}` : "_path", ), entry.expected, input, ), ), ]), ].reduce((x, y) => ts.factory.createLogicalAnd(x, y)), combiner: combiner(props.equals)(props.project)(props.importer), joiner: joiner(props.equals)(props.project)(props.importer), success: ts.factory.createTrue(), }, }); const arrow: ts.ArrowFunction = ts.factory.createArrowFunction( undefined, undefined, [ IdentifierFactory.parameter("input", TypeFactory.keyword("any")), Guardian.parameter(props.init), ], props.guard ? ts.factory.createTypePredicateNode( ts.factory.createToken(ts.SyntaxKind.AssertsKeyword), ts.factory.createIdentifier("input"), ts.factory.createTypeReferenceNode( props.name ?? TypeFactory.getFullName(props.project.checker)(props.type), ), ) : ts.factory.createTypeReferenceNode( props.name ?? TypeFactory.getFullName(props.project.checker)(props.type), ), undefined, ts.factory.createBlock( [ ts.factory.createIfStatement( ts.factory.createStrictEquality( ts.factory.createFalse(), ts.factory.createCallExpression( ts.factory.createIdentifier("__is"), undefined, [ts.factory.createIdentifier("input")], ), ), ts.factory.createBlock( [ ts.factory.createExpressionStatement( ts.factory.createBinaryExpression( ts.factory.createIdentifier("_errorFactory"), ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createIdentifier("errorFactory"), ), ), ts.factory.createExpressionStatement( ts.factory.createCallExpression( ts.factory.createArrowFunction( undefined, undefined, composed.parameters, undefined, undefined, composed.body, ), undefined, [ ts.factory.createIdentifier("input"), ts.factory.createStringLiteral("$input"), ts.factory.createTrue(), ], ), ), ], true, ), undefined, ), ...(props.guard === false ? [ ts.factory.createReturnStatement( ts.factory.createIdentifier(`input`), ), ] : []), ], true, ), ); return { functions: { ...is.functions, ...composed.functions, }, statements: [ ...is.statements, ...composed.statements, StatementFactory.constant("__is", is.arrow), StatementFactory.mut("_errorFactory"), ], arrow, }; }; export const write = (project: IProject) => (modulo: ts.LeftHandSideExpression) => (props: boolean | { equals: boolean; guard: boolean }) => (type: ts.Type, name?: string, init?: ts.Expression): ts.CallExpression => { if (typeof props === "boolean") props = { equals: props, guard: false }; const importer: FunctionImporter = new FunctionImporter(modulo.getText()); const result: FeatureProgrammer.IDecomposed = decompose({ ...props, project, importer, type, name, init, }); return FeatureProgrammer.writeDecomposed({ modulo, importer, result, }); }; const combiner = (equals: boolean) => (project: IProject) => (importer: FunctionImporter): CheckerProgrammer.IConfig.Combiner => (explore: CheckerProgrammer.IExplore) => { if (explore.tracable === false) return IsProgrammer.configure({ object: assert_object(equals)(project)(importer), numeric: true, })(project)(importer).combiner(explore); const path: string = explore.postfix ? `_path + ${explore.postfix}` : "_path"; return (logic) => (input, binaries, expected) => logic === "and" ? binaries .map((binary) => binary.combined ? binary.expression : ts.factory.createLogicalOr( binary.expression, create_guard_call(importer)( explore.source === "top" ? ts.factory.createTrue() : ts.factory.createIdentifier("_exceptionable"), )(ts.factory.createIdentifier(path), expected, input), ), ) .reduce(ts.factory.createLogicalAnd) : ts.factory.createLogicalOr( binaries .map((binary) => binary.expression) .reduce(ts.factory.createLogicalOr), create_guard_call(importer)( explore.source === "top" ? ts.factory.createTrue() : ts.factory.createIdentifier("_exceptionable"), )(ts.factory.createIdentifier(path), expected, input), ); // : (() => { // const addicted = binaries.slice(); // if ( // addicted[addicted.length - 1]!.combined === false // ) { // addicted.push({ // combined: true, // expression: create_guard_call(importer)( // explore.source === "top" // ? ts.factory.createTrue() // : ts.factory.createIdentifier( // "_exceptionable", // ), // )( // ts.factory.createIdentifier(path), // expected, // input, // ), // }); // } // return addicted // .map((b) => b.expression) // .reduce(ts.factory.createLogicalOr); // })(); }; const assert_object = (equals: boolean) => (project: IProject) => (importer: FunctionImporter) => check_object({ equals, assert: true, undefined: true, reduce: ts.factory.createLogicalAnd, positive: ts.factory.createTrue(), superfluous: (value) => create_guard_call(importer)()( ts.factory.createAdd( ts.factory.createIdentifier("_path"), ts.factory.createCallExpression(importer.use("join"), undefined, [ ts.factory.createIdentifier("key"), ]), ), "undefined", value, ), halt: (expr) => ts.factory.createLogicalOr( ts.factory.createStrictEquality( ts.factory.createFalse(), ts.factory.createIdentifier("_exceptionable"), ), expr, ), })(project)(importer); const joiner = (equals: boolean) => (project: IProject) => (importer: FunctionImporter): CheckerProgrammer.IConfig.IJoiner => ({ object: assert_object(equals)(project)(importer), array: (input, arrow) => ts.factory.createCallExpression( IdentifierFactory.access(input)("every"), undefined, [arrow], ), failure: (value, expected, explore) => create_guard_call(importer)( explore?.from === "top" ? ts.factory.createTrue() : ts.factory.createIdentifier("_exceptionable"), )( ts.factory.createIdentifier( explore?.postfix ? `_path + ${explore.postfix}` : "_path", ), expected, value, ), full: equals ? undefined : (condition) => (input, expected, explore) => ts.factory.createLogicalOr( condition, create_guard_call(importer)( explore.from === "top" ? ts.factory.createTrue() : ts.factory.createIdentifier("_exceptionable"), )(ts.factory.createIdentifier("_path"), expected, input), ), }); const create_guard_call = (importer: FunctionImporter) => (exceptionable?: ts.Expression) => ( path: ts.Expression, expected: string, value: ts.Expression, ): ts.Expression => ts.factory.createCallExpression(importer.use("guard"), undefined, [ exceptionable ?? ts.factory.createIdentifier("_exceptionable"), ts.factory.createObjectLiteralExpression( [ ts.factory.createPropertyAssignment("path", path), ts.factory.createPropertyAssignment( "expected", ts.factory.createStringLiteral(expected), ), ts.factory.createPropertyAssignment("value", value), ], true, ), ts.factory.createIdentifier("_errorFactory"), ]); export namespace Guardian { export const identifier = () => ts.factory.createIdentifier("errorFactory"); export const parameter = (init: ts.Expression | undefined) => IdentifierFactory.parameter( "errorFactory", type(), init ?? ts.factory.createToken(ts.SyntaxKind.QuestionToken), ); export const type = () => ts.factory.createFunctionTypeNode( undefined, [ ts.factory.createParameterDeclaration( undefined, undefined, ts.factory.createIdentifier("p"), undefined, ts.factory.createImportTypeNode( ts.factory.createLiteralTypeNode( ts.factory.createStringLiteral("typia"), ), undefined, ts.factory.createQualifiedName( ts.factory.createIdentifier("TypeGuardError"), ts.factory.createIdentifier("IProps"), ), undefined, false, ), undefined, ), ], ts.factory.createTypeReferenceNode( ts.factory.createIdentifier("Error"), undefined, ), ); } }