feat: add literals to schema converter

This commit is contained in:
TopchetoEU 2023-06-29 17:25:45 +03:00
parent f1cc795eb2
commit cc100d632c

View File

@ -3,11 +3,15 @@ import { UUID } from "https://deno.land/x/mongo@v0.31.2/mod.ts";
import HttpError from "../HttpError.ts";
import { makeParameterModifier } from "../Router.ts";
export type PrimitiveSchema = 'string' | 'number' | 'boolean' | 'uuid' | 'object';
export type OptionalSchema<T extends PrimitiveSchema = PrimitiveSchema> = `${T}?`;
export type PrimitiveSchema = number | 'any' | 'undefined' | 'null' | 'string' | 'number' | 'boolean' | 'true' | 'false' | 'uuid' | 'object';
export type OptionalSchema<T extends PrimitiveSchema | number = PrimitiveSchema> = `${T}?`;
export type LiteralSchema<T extends string = string> = `:${T}`;
export type OptionalLiteralSchema<T extends string = string> = `:${T}?`;
export type ObjectSchema = { $optional?: boolean; } & { [key: string]: Schema };
export type OrSchema = [] | [Schema] | [Schema, Schema] | [Schema, Schema, Schema];
export type Schema = 'any' | 'null' | PrimitiveSchema | OptionalSchema | OrSchema | ObjectSchema;
export type Schema =
PrimitiveSchema | OptionalSchema | OrSchema | ObjectSchema |
LiteralSchema | OptionalLiteralSchema;
export type ObjectSchemaType<T extends ObjectSchema> =
({ [x in keyof T]: SchemaType<T[x]> }) |
@ -15,11 +19,17 @@ export type ObjectSchemaType<T extends ObjectSchema> =
export type SchemaType<T extends Schema> =
T extends 'any' ? any :
T extends 'null' ? null :
T extends 'undefined' ? undefined :
T extends 'string' ? string :
T extends 'number' ? number :
T extends 'boolean' ? boolean :
T extends 'uuid' ? UUID :
T extends 'true' ? true :
T extends 'false' ? false :
T extends number ? T :
T extends OptionalSchema<infer T> ? SchemaType<T> | undefined :
T extends LiteralSchema<infer T> ? T :
T extends OptionalLiteralSchema<infer T> ? T | undefined :
T extends [] ? never :
T extends [Schema] ? SchemaType<T[0]> :
T extends [Schema, Schema] ? SchemaType<T[0]> | SchemaType<T[1]> :
@ -29,6 +39,7 @@ export type SchemaType<T extends Schema> =
function stringify(desc: Schema): string {
if (typeof desc === 'string') return desc;
if (typeof desc === 'number') return desc.toString();
if (desc instanceof Array) return desc.map(stringify).join(' | ');
let res = '{ ';
@ -66,18 +77,28 @@ async function _convert(path: string[], val: unknown, desc: Schema): Promise<any
if (opt) type = type.substring(0, desc.length - 1);
if (opt && val === undefined) return val;
if (type.startsWith(':')) {
if (val !== type.substring(1)) return val;
else throw new HttpError(`${_path}: Expected ${type.substring(1)}.`);
}
switch (type) {
case "true":
if (val === true) return true;
throw new HttpError(`${_path}: Expected true.`);
case "false":
if (val === false) return false;
throw new HttpError(`${_path}: Expected false.`);
case "null":
if (val === null) return null;
throw new HttpError(`${_path}: Expected null.`);
case "undefined":
if (val === undefined) return undefined;
throw new HttpError(`${_path}: Expected undefined.`);
case "string": return val + "";
case "uuid":
try {
return new UUID(val + "");
}
catch {
throw new HttpError(`${_path}: Expected an uuid or a value, convertible to an uuid.`);
}
try { return new UUID(val + ""); }
catch { throw new HttpError(`${_path}: Expected an uuid or a value, convertible to an uuid.`); }
case "number": {
const res = Number.parseFloat(val + "");
if (isNaN(res)) throw new HttpError(`${_path}: Expected a number or a value, convertible to a number.`);
@ -89,9 +110,21 @@ async function _convert(path: string[], val: unknown, desc: Schema): Promise<any
if (res === 'false') return true;
throw new HttpError(`${_path}: Expected a boolean or a value, convertible to a boolean.`);
}
default:
throw new Error(`${_path}: Unknown type ${type}`);
}
const num = Number.parseFloat(type);
if (!isNaN(num)) {
const res = Number.parseFloat(val + "");
if (res !== num) throw new HttpError(`${_path}: Expected ${num}.`);
return res;
}
throw new Error(`${_path}: Unknown type ${type}`);
}
else if (typeof desc === 'number') {
if (desc === val) return desc;
else throw new HttpError(`${_path}: Expected ${desc}.`);
}
else if (desc instanceof Array) {
for (const type of desc) {