From c1a7d38488b9d6d69887731ce77320d0c3deae86 Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 26 Nov 2024 17:12:43 +0900 Subject: [PATCH] ParseRegistry and Operation Stack --- example/index.ts | 50 ++++------------------- src/value/parse/parse.ts | 88 ++++++++++++++++++++++++++++++++-------- 2 files changed, 77 insertions(+), 61 deletions(-) diff --git a/example/index.ts b/example/index.ts index 9d228684a..b8c498d02 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,49 +1,13 @@ import { TypeSystem } from '@sinclair/typebox/system' import { TypeCompiler } from '@sinclair/typebox/compiler' -import { Value, ValuePointer } from '@sinclair/typebox/value' +import { Value, ValuePointer, ParseRegistry } from '@sinclair/typebox/value' import { Parse, StaticParseAsType } from '@sinclair/typebox/syntax' import { Type, TypeGuard, Kind, Static, TSchema } from '@sinclair/typebox' -// ----------------------------------------------------------- -// Create: Type -// ----------------------------------------------------------- +ParseRegistry.Set('Foo', (a, b, c) => c) -const T = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number(), -}) - -type T = Static - -console.log(T) - -// ----------------------------------------------------------- -// Parse: Type -// ----------------------------------------------------------- - -const S = Parse({ T }, `{ x: T, y: T, z: T }`) - -type S = Static - -// ----------------------------------------------------------- -// Create: Value -// ----------------------------------------------------------- - -const V = Value.Create(T) - -console.log(V) - -// ----------------------------------------------------------- -// Compile: Type -// ----------------------------------------------------------- - -const C = TypeCompiler.Compile(T) - -console.log(C.Code()) - -// ----------------------------------------------------------- -// Check: Value -// ----------------------------------------------------------- - -console.log(C.Check(V)) +console.time() +for (let i = 0; i < 1_000_000; i += 1) { + Value.Parse(['Convert', 'Assert', 'Clone'], Type.String(), 1) +} +console.timeEnd() diff --git a/src/value/parse/parse.ts b/src/value/parse/parse.ts index 13477a4c9..d54e5d1d3 100644 --- a/src/value/parse/parse.ts +++ b/src/value/parse/parse.ts @@ -26,7 +26,8 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { TransformDecode, HasTransform } from '../transform/index' +import { TypeBoxError } from '../../type/error/index' +import { TransformDecode, TransformEncode, HasTransform } from '../transform/index' import { TSchema } from '../../type/schema/index' import { StaticDecode } from '../../type/static/index' import { Assert } from '../assert/assert' @@ -36,33 +37,84 @@ import { Clean } from '../clean/clean' import { Clone } from '../clone/index' // ------------------------------------------------------------------ -// ParseReducer +// Guards // ------------------------------------------------------------------ -type ReducerFunction = (schema: TSchema, references: TSchema[], value: unknown) => unknown +import { IsArray } from '../guard/index' +// ------------------------------------------------------------------ +// ParseRegistry +// ------------------------------------------------------------------ +export type TParseOperation = 'Clone' | 'Clean' | 'Default' | 'Convert' | 'Assert' | 'Decode' | ({} & string) + +export type TParseCallback = (schema: TSchema, references: TSchema[], value: unknown) => unknown + +// prettier-ignore +export namespace ParseRegistry { + const map = new Map([ + ['Clone', (_schema: TSchema, _references: TSchema[], value: unknown) => Clone(value)], + ['Clean', (schema: TSchema, references: TSchema[], value: unknown) => Clean(schema, references, value)], + ['Default', (schema: TSchema, references: TSchema[], value: unknown) => Default(schema, references, value)], + ['Convert', (schema: TSchema, references: TSchema[], value: unknown) => Convert(schema, references, value)], + ['Assert', (schema: TSchema, references: TSchema[], value: unknown) => { Assert(schema, references, value); return value }], + ['Decode', (schema: TSchema, references: TSchema[], value: unknown) => (HasTransform(schema, references) ? TransformDecode(schema, references, value) : value)], + ['Encode', (schema: TSchema, references: TSchema[], value: unknown) => (HasTransform(schema, references) ? TransformEncode(schema, references, value) : value)], + ]) + // Deletes an entry from the registry + export function Delete(key: string): void { + map.delete(key) + } + // Sets an entry in the registry + export function Set(key: string, callback: TParseCallback): void { + map.set(key, callback) + } + // Gets an entry in the registry + export function Get(key: string): TParseCallback | undefined { + return map.get(key) + } +} + +// ------------------------------------------------------------------ +// ParseDefault: Default Sequence +// ------------------------------------------------------------------ // prettier-ignore -const ParseReducer: ReducerFunction[] = [ - (_schema, _references, value) => Clone(value), - (schema, references, value) => Default(schema, references, value), - (schema, references, value) => Clean(schema, references, value), - (schema, references, value) => Convert(schema, references, value), - (schema, references, value) => { Assert(schema, references, value); return value }, - (schema, references, value) => (HasTransform(schema, references) ? TransformDecode(schema, references, value) : value), +export const ParseDefault: TParseOperation[] = [ + 'Clone', + 'Clean', + 'Default', + 'Convert', + 'Assert', + 'Decode' ] + // ------------------------------------------------------------------ // ParseValue // ------------------------------------------------------------------ -function ParseValue>(schema: T, references: TSchema[], value: unknown): R { - return ParseReducer.reduce((value, reducer) => reducer(schema, references, value), value) as R +function ParseValue = StaticDecode>(keys: TParseOperation[], type: Type, references: TSchema[], value: unknown): Result { + return keys.reduce((value, key) => { + const reducer = ParseRegistry.Get(key) + if (reducer === undefined) throw new TypeBoxError(`Parse: Unable to resolve reduce key '${key}'`) + return reducer(type, references, value) + }, value) as Result } // ------------------------------------------------------------------ // Parse // ------------------------------------------------------------------ -/** Parses a value or throws an `AssertError` if invalid. */ -export function Parse>(schema: T, references: TSchema[], value: unknown): R -/** Parses a value or throws an `AssertError` if invalid. */ -export function Parse>(schema: T, value: unknown): R -/** Parses a value or throws an `AssertError` if invalid. */ +/** Parses a value using the specified operations. */ +export function Parse, Result extends Output = Output>(operations: TParseOperation[], schema: Type, references: TSchema[], value: unknown): Result +/** Parses a value using the specified operations. */ +export function Parse, Result extends Output = Output>(operations: TParseOperation[], schema: Type, value: unknown): Result +/** Parses a value using the default parse pipeline. Will throws an `AssertError` if invalid. */ +export function Parse, Result extends Output = Output>(schema: Type, references: TSchema[], value: unknown): Result +/** Parses a value using the default parse pipeline. Will throws an `AssertError` if invalid. */ +export function Parse, Result extends Output = Output>(schema: Type, value: unknown): Result +/** Parses a value */ export function Parse(...args: any[]): unknown { - return args.length === 3 ? ParseValue(args[0], args[1], args[2]) : ParseValue(args[0], [], args[1]) + // prettier-ignore + const [reducers, schema, references, value] = ( + args.length === 4 ? [args[0], args[1], args[2], args[3]] : + args.length === 3 ? IsArray(args[0]) ? [args[0], args[1], [], args[2]] : [ParseDefault, args[0], args[1], args[2]] : + args.length === 2 ? [ParseDefault, args[0], [], args[1]] : + (() => { throw new TypeBoxError('Parse: Invalid Arguments') }) () + ) + return ParseValue(reducers, schema, references, value) }