Skip to content

Commit

Permalink
Allow unions of transformations and checks
Browse files Browse the repository at this point in the history
  • Loading branch information
GoogleFeud committed Apr 28, 2024
1 parent fe0298f commit 4caa4cc
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 48 deletions.
33 changes: 1 addition & 32 deletions src/gen/nodes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
} from "../expressionUtils";
import {CodeReferenceKind, Transformer} from "../../transformer";
import {TransformerError, genCheckCtx, isSingleIfStatement} from "../../utils";
import { getUnionMembers } from "../../utils/unions";

export interface ValidationResultType {
throw?: string | ts.Symbol;
Expand Down Expand Up @@ -484,38 +485,6 @@ export function isNullableNode(validator: Validator): ts.Expression {
return _bin(validator.expression(), UNDEFINED, ts.SyntaxKind.ExclamationEqualsEqualsToken);
}

export function getUnionMembers(validators: Validator[]): {
compound: Validator[];
normal: Validator[];
object: [Validator, Validator][];
isNullable: boolean;
} {
const compoundTypes: Validator[] = [],
normalTypes: Validator[] = [],
objectTypes: [Validator, Validator][] = [];
let isNullable = false;
const objectKind = validators.filter(v => v.typeData.kind === TypeDataKinds.Object).length;
for (const child of validators) {
if (child.typeData.kind === TypeDataKinds.Undefined) {
isNullable = true;
continue;
} else if (child.children.length && child.typeData.kind === TypeDataKinds.Object && objectKind > 1) {
const idRepresent = child.getFirstLiteralChild();
if (idRepresent) {
child.children.splice(child.children.indexOf(idRepresent), 1);
objectTypes.push([child, idRepresent]);
} else compoundTypes.push(child);
} else if (child.isComplexType()) compoundTypes.push(child);
else normalTypes.push(child);
}
return {
compound: compoundTypes,
normal: normalTypes,
object: objectTypes,
isNullable
};
}

export function genStatements(results: GenResult[], ctx: NodeGenContext): ts.Statement[] {
const result = [];
for (const genResult of results) {
Expand Down
3 changes: 2 additions & 1 deletion src/gen/nodes/match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import ts from "typescript";
import {Transformer} from "../../transformer";
import {TypeDataKinds, Validator, genValidator} from "../validators";
import {UNDEFINED, _access, _and, _arr_check, _arrow_fn, _bin, _bool, _call, _ident, _not, _obj_check, _or, BlockLike, _if_chain, _var} from "../expressionUtils";
import {GenResult, NodeGenContext, createContext, genChecks, genNode, getUnionMembers} from "./";
import {GenResult, NodeGenContext, createContext, genChecks, genNode} from "./";
import {doesAlwaysReturn} from "../../utils";
import { getUnionMembers } from "../../utils/unions";

export interface MatchArm {
parameter: ts.ParameterDeclaration;
Expand Down
31 changes: 27 additions & 4 deletions src/gen/nodes/transform.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {createContext, genNode} from ".";
import {Transformer} from "../../transformer";
import {genCheckCtx} from "../../utils";
import {_access, _assign, _call, _for, _ident, _obj, _stmt, _var} from "../expressionUtils";
import {TypeDataKinds, Validator} from "../validators";
import {getUnionMembers} from "../../utils/unions";
import {_access, _assign, _call, _for, _ident, _if_chain, _not, _obj, _stmt, _var} from "../expressionUtils";
import {TransformTypeData, TypeDataKinds, Validator} from "../validators";
import ts from "typescript";

export interface TransformCtx {
Expand All @@ -10,11 +12,11 @@ export interface TransformCtx {
}

export function genTransform(validator: Validator, target: ts.Expression, ctx: TransformCtx): ts.Statement[] {
const assignTarget = validator.parent ? _access(target, validator.name) : target;
const assignTarget = validator.parent && validator.name !== "" ? _access(target, validator.name) : target;

switch (validator.typeData.kind) {
case TypeDataKinds.Transform: {
if (!validator.typeData.transformations) return [_stmt(ts.factory.createNull())];
if (!validator.typeData.transformations.length) return [];
const prevStmts = [];
let previousExp = validator.expression();
for (let i = 0; i < validator.typeData.transformations.length; i++) {
Expand Down Expand Up @@ -52,6 +54,27 @@ export function genTransform(validator: Validator, target: ts.Expression, ctx: T
}
return [_stmt(_assign(assignTarget, ts.factory.createArrayLiteralExpression())), _for(validator.expression(), index, genTransform(childType, assignTarget, ctx))[0]];
}
case TypeDataKinds.Union: {
const transforms = validator.getChildrenOfKind(TypeDataKinds.Transform);
if (!transforms.length) return [_stmt(_assign(assignTarget, validator.expression()))];
const transformBases = transforms.map(transform => (transform.typeData as TransformTypeData).rest).filter(i => i) as Validator[];

const { normal, compound } = getUnionMembers(transformBases);

const nodeCtx = createContext(ctx.transformer, {none: true}, ctx.origin);

return [
_if_chain(
0,
[...normal, ...compound].map(validator => {
const check = genNode(validator, nodeCtx);
const originalTransform = transforms[transformBases.indexOf(validator)] as Validator;

return [_not(check.condition), genTransform(originalTransform, assignTarget, ctx)];
})
) as ts.Statement
];
}
default:
return [_stmt(_assign(assignTarget, validator.expression()))];
}
Expand Down
4 changes: 2 additions & 2 deletions src/gen/validators/genValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ export function genValidator(transformer: Transformer, type: ts.Type | undefined
else return;
} else if (type.getCallSignatures().length === 1) return new Validator(type, name, {kind: TypeDataKinds.Function}, exp, parent);
else {
if (type.getProperty("__$transform")) {
if (type.isUnion()) return createUnionValidator(type, type.types, transformer, name, exp, parent);
else if (type.getProperty("__$transform")) {
const expressions: CodeReference[] = [];
let rest: Validator | undefined;

Expand Down Expand Up @@ -108,7 +109,6 @@ export function genValidator(transformer: Transformer, type: ts.Type | undefined
}
const utility = transformer.getPropType(type, "name");
if (!utility || !utility.isStringLiteral()) {
if (type.isUnion()) return createUnionValidator(type, type.types, transformer, name, exp, parent);
const properties = (parent: Validator) =>
type.getProperties().map(sym => {
const typeOfProp = (transformer.checker.getTypeOfSymbol(sym) || transformer.checker.getNullType()) as ts.Type;
Expand Down
9 changes: 0 additions & 9 deletions src/gen/validators/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,15 +277,6 @@ export class Validator {
return result;
}

areChildrenSameKind(): boolean {
if (!this.children.length) return true;
const firstChildKind = (this.children[0] as Validator).typeData.kind;
for (let i = 1; i < this.children.length; i++) {
if ((this.children[i] as Validator).typeData.kind !== firstChildKind) return false;
}
return true;
}

getFirstLiteralChild(): Validator | undefined {
for (const child of this.children) {
if ((child.typeData.kind === TypeDataKinds.String && child.typeData.literal !== undefined) || (child.typeData.kind === TypeDataKinds.Number && child.typeData.literal !== undefined))
Expand Down
33 changes: 33 additions & 0 deletions src/utils/unions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Validator, TypeDataKinds } from "../gen/validators";

export function getUnionMembers(validators: Validator[]): {
compound: Validator[];
normal: Validator[];
object: [Validator, Validator][];
isNullable: boolean;
} {
const compoundTypes: Validator[] = [],
normalTypes: Validator[] = [],
objectTypes: [Validator, Validator][] = [];
let isNullable = false;
const objectKind = validators.filter(v => v.typeData.kind === TypeDataKinds.Object).length;
for (const child of validators) {
if (child.typeData.kind === TypeDataKinds.Undefined) {
isNullable = true;
continue;
} else if (child.children.length && child.typeData.kind === TypeDataKinds.Object && objectKind > 1) {
const idRepresent = child.getFirstLiteralChild();
if (idRepresent) {
child.children.splice(child.children.indexOf(idRepresent), 1);
objectTypes.push([child, idRepresent]);
} else compoundTypes.push(child);
} else if (child.isComplexType()) compoundTypes.push(child);
else normalTypes.push(child);
}
return {
compound: compoundTypes,
normal: normalTypes,
object: objectTypes,
isNullable
};
}

0 comments on commit 4caa4cc

Please sign in to comment.