Skip to content

Commit 082e4b8

Browse files
committed
feat(json-type): 🎸 emit "map" type validation codegen
1 parent e913ba3 commit 082e4b8

File tree

3 files changed

+46
-17
lines changed

3 files changed

+46
-17
lines changed

src/json-type/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export enum ValidationError {
66
ARR,
77
TUP,
88
OBJ,
9+
MAP,
910
KEY,
1011
KEYS,
1112
BIN,
@@ -32,6 +33,7 @@ export const ValidationErrorMessage = {
3233
[ValidationError.ARR]: 'Not an array.',
3334
[ValidationError.TUP]: 'Not a tuple.',
3435
[ValidationError.OBJ]: 'Not an object.',
36+
[ValidationError.MAP]: 'Not a map.',
3537
[ValidationError.KEY]: 'Missing key.',
3638
[ValidationError.KEYS]: 'Too many or missing object keys.',
3739
[ValidationError.BIN]: 'Not a binary.',

src/json-type/type/__tests__/validateTestSuite.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,37 @@ export const validateTestSuite = (validate: (type: Type, value: unknown) => void
417417
});
418418
});
419419

420+
describe('map', () => {
421+
test('accepts empty object as input', () => {
422+
const type = t.map;
423+
validate(type, {});
424+
});
425+
426+
test('does not accept empty array as input', () => {
427+
const type = t.map;
428+
expect(() => validate(type, [])).toThrow();
429+
});
430+
431+
test('validates "any" map', () => {
432+
const type = t.map;
433+
validate(type, {
434+
a: 'str',
435+
b: 123,
436+
c: true,
437+
});
438+
});
439+
440+
test('validates contained type', () => {
441+
const type = t.Map(t.str);
442+
validate(type, {});
443+
validate(type, {a: ''});
444+
validate(type, {b: 'asdf'});
445+
expect(() => validate(type, {c: 123})).toThrowErrorMatchingInlineSnapshot(`"STR"`);
446+
expect(() => validate(type, {c: false})).toThrowErrorMatchingInlineSnapshot(`"STR"`);
447+
expect(() => validate(type, [])).toThrowErrorMatchingInlineSnapshot(`"MAP"`);
448+
});
449+
});
450+
420451
describe('ref', () => {
421452
test('validates after recursively resolving', () => {
422453
const t = system.t;

src/json-type/type/classes.ts

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1916,22 +1916,18 @@ export class MapType<T extends Type> extends AbstractType<schema.MapSchema<Schem
19161916
}
19171917

19181918
public codegenValidator(ctx: ValidatorCodegenContext, path: ValidationPath, r: string): void {
1919-
throw new Error('TODO');
1920-
// const rl = ctx.codegen.getRegister();
1921-
// const ri = ctx.codegen.getRegister();
1922-
// const rv = ctx.codegen.getRegister();
1923-
// const err = ctx.err(ValidationError.ARR, path);
1924-
// const errLen = ctx.err(ValidationError.ARR_LEN, path);
1925-
// const {min, max} = this.schema;
1926-
// ctx.js(/* js */ `if (!Array.isArray(${r})) return ${err};`);
1927-
// ctx.js(`var ${rl} = ${r}.length;`);
1928-
// if (min !== undefined) ctx.js(`if (${rl} < ${min}) return ${errLen};`);
1929-
// if (max !== undefined) ctx.js(`if (${rl} > ${max}) return ${errLen};`);
1930-
// ctx.js(`for (var ${rv}, ${ri} = ${r}.length; ${ri}-- !== 0;) {`);
1931-
// ctx.js(`${rv} = ${r}[${ri}];`);
1932-
// this.type.codegenValidator(ctx, [...path, {r: ri}], rv);
1933-
// ctx.js(`}`);
1934-
// ctx.emitCustomValidators(this, path, r);
1919+
const err = ctx.err(ValidationError.MAP, path);
1920+
ctx.js(`if (!${r} || (typeof ${r} !== 'object') || (${r}.constructor !== Object)) return ${err};`);
1921+
const rKeys = ctx.codegen.var(`Object.keys(${r});`);
1922+
const rLength = ctx.codegen.var(`${rKeys}.length`);
1923+
const rKey = ctx.codegen.r();
1924+
const rValue = ctx.codegen.r();
1925+
ctx.js(`for (var ${rKey}, ${rValue}, i = 0; i < ${rLength}; i++) {`);
1926+
ctx.js(`${rKey} = ${rKeys}[i];`);
1927+
ctx.js(`${rValue} = ${r}[${rKey}];`);
1928+
this.type.codegenValidator(ctx, [...path, {r: rKey}], rValue);
1929+
ctx.js(`}`);
1930+
ctx.emitCustomValidators(this, path, r);
19351931
}
19361932

19371933
public codegenJsonTextEncoder(ctx: JsonTextEncoderCodegenContext, value: JsExpression): void {
@@ -2018,7 +2014,7 @@ export class MapType<T extends Type> extends AbstractType<schema.MapSchema<Schem
20182014
}
20192015

20202016
public codegenCapacityEstimator(ctx: CapacityEstimatorCodegenContext, value: JsExpression): void {
2021-
throw new Error('TODO');
2017+
throw new Error('TODO CAP');
20222018
// const codegen = ctx.codegen;
20232019
// ctx.inc(MaxEncodingOverhead.Array);
20242020
// const rLen = codegen.var(`${value.use()}.length`);

0 commit comments

Comments
 (0)