Skip to content

Commit

Permalink
Tuple Transform Check | Check Both Value and Compiler Encoding in Tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sinclairzx81 committed Nov 18, 2023
1 parent 5bd9f12 commit 1d5cd5b
Show file tree
Hide file tree
Showing 34 changed files with 344 additions and 284 deletions.
4 changes: 2 additions & 2 deletions src/compiler/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ export class TypeCheck<T extends Types.TSchema> {
/** Decodes a value or throws if error */
public Decode(value: unknown): Types.StaticDecode<T> {
if (!this.checkFunc(value)) throw new TransformDecodeCheckError(this.schema, value, this.Errors(value).First()!)
return this.hasTransform ? DecodeTransform.Decode(this.schema, this.references, value, (_, __, value) => this.Check(value)) : value
return this.hasTransform ? DecodeTransform.Decode(this.schema, this.references, value) : value
}
/** Encodes a value or throws if error */
public Encode(value: unknown): Types.StaticEncode<T> {
const encoded = this.hasTransform ? EncodeTransform.Encode(this.schema, this.references, value, (_, __, value) => this.Check(value)) : value
const encoded = this.hasTransform ? EncodeTransform.Encode(this.schema, this.references, value) : value
if (!this.checkFunc(encoded)) throw new TransformEncodeCheckError(this.schema, value, this.Errors(value).First()!)
return encoded
}
Expand Down
19 changes: 8 additions & 11 deletions src/value/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ THE SOFTWARE.
---------------------------------------------------------------------------*/

import { IsString, IsPlainObject, IsArray, IsValueType } from './guard'
import { IsString, IsPlainObject, IsArray, IsValueType, IsUndefined } from './guard'
import { ValueError } from '../errors/errors'
import { Deref } from './deref'
import { Check } from './check'
import * as Types from '../typebox'

// -------------------------------------------------------------------------
Expand Down Expand Up @@ -112,7 +113,7 @@ export namespace HasTransform {
return Visit(Deref(schema, references), references)
}
function TTuple(schema: Types.TTuple<any[]>, references: Types.TSchema[]) {
return Types.TypeGuard.TTransform(schema) || (Types.TypeGuard.TSchema(schema.items) && schema.items.some((schema) => Visit(schema, references)))
return Types.TypeGuard.TTransform(schema) || (!IsUndefined(schema.items) && schema.items.some((schema) => Visit(schema, references)))
}
function TUnion(schema: Types.TUnion, references: Types.TSchema[]) {
return Types.TypeGuard.TTransform(schema) || schema.anyOf.some((schema) => Visit(schema, references))
Expand Down Expand Up @@ -262,7 +263,7 @@ export namespace DecodeTransform {
function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any) {
const value1 = Default(schema, value)
for (const subschema of schema.anyOf) {
if (!checkFunction(subschema, references, value1)) continue
if (!Check(subschema, references, value1)) continue
return Visit(subschema, references, value1)
}
return value1
Expand Down Expand Up @@ -323,9 +324,7 @@ export namespace DecodeTransform {
return Default(schema_, value)
}
}
let checkFunction: CheckFunction = () => false
export function Decode(schema: Types.TSchema, references: Types.TSchema[], value: unknown, check: CheckFunction): unknown {
checkFunction = check
export function Decode(schema: Types.TSchema, references: Types.TSchema[], value: unknown): unknown {
return Visit(schema, references, value)
}
}
Expand Down Expand Up @@ -405,14 +404,14 @@ export namespace EncodeTransform {
function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any) {
// test value against union variants
for (const subschema of schema.anyOf) {
if (!checkFunction(subschema, references, value)) continue
if (!Check(subschema, references, value)) continue
const value1 = Visit(subschema, references, value)
return Default(schema, value1)
}
// test transformed value against union variants
for (const subschema of schema.anyOf) {
const value1 = Visit(subschema, references, value)
if (!checkFunction(schema, references, value1)) continue
if (!Check(schema, references, value1)) continue
return Default(schema, value1)
}
return Default(schema, value)
Expand Down Expand Up @@ -472,9 +471,7 @@ export namespace EncodeTransform {
return Default(schema_, value)
}
}
let checkFunction: CheckFunction = () => false
export function Encode(schema: Types.TSchema, references: Types.TSchema[], value: unknown, check: CheckFunction): unknown {
checkFunction = check
export function Encode(schema: Types.TSchema, references: Types.TSchema[], value: unknown): unknown {
return Visit(schema, references, value)
}
}
12 changes: 6 additions & 6 deletions src/value/value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,23 +78,23 @@ export namespace Value {
return ValueClone.Clone(value)
}
/** Decodes a value or throws if error */
export function Decode<T extends Types.TSchema, D = Types.StaticDecode<T>>(schema: T, references: Types.TSchema[], value: unknown): D
export function Decode<T extends Types.TSchema, R = Types.StaticDecode<T>>(schema: T, references: Types.TSchema[], value: unknown): R
/** Decodes a value or throws if error */
export function Decode<T extends Types.TSchema, D = Types.StaticDecode<T>>(schema: T, value: unknown): D
export function Decode<T extends Types.TSchema, R = Types.StaticDecode<T>>(schema: T, value: unknown): R
/** Decodes a value or throws if error */
export function Decode(...args: any[]) {
const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]]
if (!Check(schema, references, value)) throw new ValueTransform.TransformDecodeCheckError(schema, value, Errors(schema, references, value).First()!)
return ValueTransform.DecodeTransform.Decode(schema, references, value, ValueCheck.Check)
return ValueTransform.DecodeTransform.Decode(schema, references, value)
}
/** Encodes a value or throws if error */
export function Encode<T extends Types.TSchema, E = Types.StaticEncode<T>>(schema: T, references: Types.TSchema[], value: unknown): E
export function Encode<T extends Types.TSchema, R = Types.StaticEncode<T>>(schema: T, references: Types.TSchema[], value: unknown): R
/** Encodes a value or throws if error */
export function Encode<T extends Types.TSchema, E = Types.StaticEncode<T>>(schema: T, value: unknown): E
export function Encode<T extends Types.TSchema, R = Types.StaticEncode<T>>(schema: T, value: unknown): R
/** Encodes a value or throws if error */
export function Encode(...args: any[]) {
const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]]
const encoded = ValueTransform.EncodeTransform.Encode(schema, references, value, ValueCheck.Check)
const encoded = ValueTransform.EncodeTransform.Encode(schema, references, value)
if (!Check(schema, references, encoded)) throw new ValueTransform.TransformEncodeCheckError(schema, value, Errors(schema, references, value).First()!)
return encoded
}
Expand Down
33 changes: 33 additions & 0 deletions test/runtime/value/transform/_encoder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { IsAsyncIterator, IsIterator, IsFunction, IsSymbol } from '@sinclair/typebox/value/guard'
import { TSchema, StaticDecode, StaticEncode } from '@sinclair/typebox'
import { TypeCompiler } from '@sinclair/typebox/compiler'
import { Value } from '@sinclair/typebox/value'
import { Assert } from '../../assert/index'

function AssertSame(actual: unknown, expect: unknown) {
if (IsAsyncIterator(actual) && IsAsyncIterator(expect)) return
if (IsIterator(actual) && IsIterator(expect)) return
if (IsSymbol(actual) && IsSymbol(expect)) return
if (IsFunction(actual) && IsFunction(expect)) return
Assert.IsEqual(actual, expect)
}

export function Decode<T extends TSchema, R = StaticDecode<T>>(schema: T, references: TSchema[], value: unknown): R
export function Decode<T extends TSchema, R = StaticDecode<T>>(schema: T, value: unknown): R
export function Decode(...args: any[]) {
const [schema, references, value] = args.length === 2 ? [args[0], [], args[1]] : [args[0], args[1], args[2]]
const value1 = TypeCompiler.Compile(schema as TSchema, references).Decode(value)
const value2 = Value.Decode(schema as TSchema, references, value)
AssertSame(value1, value2)
return value2
}

export function Encode<T extends TSchema, R = StaticEncode<T>>(schema: T, references: TSchema[], value: unknown): R
export function Encode<T extends TSchema, R = StaticEncode<T>>(schema: T, value: unknown): R
export function Encode(...args: any[]) {
const [schema, references, value] = args.length === 2 ? [args[0], [], args[1]] : [args[0], args[1], args[2]]
const value1 = TypeCompiler.Compile(schema as TSchema, references).Encode(value)
const value2 = Value.Encode(schema as TSchema, references, value)
AssertSame(value1, value2)
return value2
}
9 changes: 5 additions & 4 deletions test/runtime/value/transform/any.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as Encoder from './_encoder'
import { Assert } from '../../assert'
import { Value } from '@sinclair/typebox/value'
import { Type } from '@sinclair/typebox'
Expand All @@ -10,11 +11,11 @@ describe('value/transform/Any', () => {
.Decode((value) => value)
.Encode((value) => value)
it('Should decode mapped', () => {
const R = Value.Decode(T0, 123)
const R = Encoder.Decode(T0, 123)
Assert.IsEqual(R, 123)
})
it('Should encode mapped', () => {
const R = Value.Encode(T0, 123)
const R = Encoder.Encode(T0, 123)
Assert.IsEqual(R, 123)
})
// --------------------------------------------------------
Expand All @@ -24,11 +25,11 @@ describe('value/transform/Any', () => {
.Decode((value) => 1)
.Encode((value) => 2)
it('Should decode mapped', () => {
const R = Value.Decode(T1, null)
const R = Encoder.Decode(T1, null)
Assert.IsEqual(R, 1)
})
it('Should encode mapped', () => {
const R = Value.Encode(T1, null)
const R = Encoder.Encode(T1, null)
Assert.IsEqual(R, 2)
})
})
31 changes: 16 additions & 15 deletions test/runtime/value/transform/array.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as Encoder from './_encoder'
import { Assert } from '../../assert'
import { Value } from '@sinclair/typebox/value'
import { Type } from '@sinclair/typebox'
Expand All @@ -10,15 +11,15 @@ describe('value/transform/Array', () => {
.Decode((value) => value)
.Encode((value) => value)
it('Should decode mapped', () => {
const R = Value.Decode(T0, [0, 1, 2])
const R = Encoder.Decode(T0, [0, 1, 2])
Assert.IsEqual(R, [0, 1, 2])
})
it('Should encode mapped', () => {
const R = Value.Encode(T0, [0, 1, 2])
const R = Encoder.Encode(T0, [0, 1, 2])
Assert.IsEqual(R, [0, 1, 2])
})
it('Should throw on mapped decode', () => {
Assert.Throws(() => Value.Decode(T0, null))
Assert.Throws(() => Encoder.Decode(T0, null))
})
// --------------------------------------------------------
// Mapped
Expand All @@ -27,15 +28,15 @@ describe('value/transform/Array', () => {
.Decode((value) => 1)
.Encode((value) => [0, 1, 2])
it('Should decode mapped', () => {
const R = Value.Decode(T1, [])
const R = Encoder.Decode(T1, [])
Assert.IsEqual(R, 1)
})
it('Should encode mapped', () => {
const R = Value.Encode(T1, null)
const R = Encoder.Encode(T1, null)
Assert.IsEqual(R, [0, 1, 2])
})
it('Should throw on mapped decode', () => {
Assert.Throws(() => Value.Decode(T1, null))
Assert.Throws(() => Encoder.Decode(T1, null))
})
// --------------------------------------------------------
// Elements
Expand All @@ -45,15 +46,15 @@ describe('value/transform/Array', () => {
.Encode((value) => (value === 'TRUE' ? true : false))
const T2 = Type.Array(B2)
it('Should decode elements', () => {
const R = Value.Decode(T2, [true, false])
const R = Encoder.Decode(T2, [true, false])
Assert.IsEqual(R, ['TRUE', 'FALSE'])
})
it('Should encode elements', () => {
const R = Value.Encode(T2, ['TRUE', 'FALSE'])
const R = Encoder.Encode(T2, ['TRUE', 'FALSE'])
Assert.IsEqual(R, [true, false])
})
it('Should throw on elements decode', () => {
Assert.Throws(() => Value.Decode(T2, null))
Assert.Throws(() => Encoder.Decode(T2, null))
})
// --------------------------------------------------------
// Elements Contains (Not Supported)
Expand All @@ -65,14 +66,14 @@ describe('value/transform/Array', () => {
contains: N3,
})
it('Should decode contains', () => {
const R = Value.Decode(T3, [1, 2, 3])
const R = Encoder.Decode(T3, [1, 2, 3])
Assert.IsEqual(R, [1, 2, 3])
})
it('Should throw on contains encode', () => {
Assert.Throws(() => Value.Encode(T3, ['hello', 2, 3]))
Assert.Throws(() => Encoder.Encode(T3, ['hello', 2, 3]))
})
it('Should throw on contains decode', () => {
Assert.Throws(() => Value.Decode(T3, null))
Assert.Throws(() => Encoder.Decode(T3, null))
})
// ------------------------------------------------------------
// Set
Expand All @@ -81,17 +82,17 @@ describe('value/transform/Array', () => {
.Decode((value) => new Set(value))
.Encode((value) => [...value])
it('should decode set', () => {
const R = Value.Decode(T4, [1, 1, 2, 3])
const R = Encoder.Decode(T4, [1, 1, 2, 3])
Assert.IsInstanceOf(R, Set)
Assert.IsTrue(R.has(1))
Assert.IsTrue(R.has(2))
Assert.IsTrue(R.has(3))
})
it('should encode set', () => {
const R = Value.Encode(T4, new Set([1, 2, 3]))
const R = Encoder.Encode(T4, new Set([1, 2, 3]))
Assert.IsEqual(R, [1, 2, 3])
})
it('Should throw on set decode', () => {
Assert.Throws(() => Value.Decode(T4, {}))
Assert.Throws(() => Encoder.Decode(T4, {}))
})
})
13 changes: 7 additions & 6 deletions test/runtime/value/transform/async-iterator.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as Encoder from './_encoder'
import { Assert } from '../../assert'
import { Value } from '@sinclair/typebox/value'
import { Type } from '@sinclair/typebox'
Expand All @@ -10,15 +11,15 @@ describe('value/transform/AsyncIterator', () => {
.Decode((value) => value)
.Encode((value) => value)
it('Should decode identity', () => {
const R = Value.Decode(T0, (async function* (): any {})())
const R = Encoder.Decode(T0, (async function* (): any {})())
Assert.IsTrue(Symbol.asyncIterator in R)
})
it('Should encode identity', () => {
const R = Value.Encode(T0, (async function* (): any {})())
const R = Encoder.Encode(T0, (async function* (): any {})())
Assert.IsTrue(Symbol.asyncIterator in R)
})
it('Should throw on identity decode', () => {
Assert.Throws(() => Value.Decode(T0, null))
Assert.Throws(() => Encoder.Decode(T0, null))
})
// --------------------------------------------------------
// Mapped
Expand All @@ -27,14 +28,14 @@ describe('value/transform/AsyncIterator', () => {
.Decode((value) => 1)
.Encode((value) => (async function* (): any {})())
it('Should decode', () => {
const R = Value.Decode(T1, (async function* (): any {})())
const R = Encoder.Decode(T1, (async function* (): any {})())
Assert.IsEqual(R, 1)
})
it('Should encode', () => {
const R = Value.Encode(T1, null)
const R = Encoder.Encode(T1, null)
Assert.IsTrue(Symbol.asyncIterator in R)
})
it('Should throw on decode', () => {
Assert.Throws(() => Value.Decode(T1, null))
Assert.Throws(() => Encoder.Decode(T1, null))
})
})
13 changes: 7 additions & 6 deletions test/runtime/value/transform/bigint.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as Encoder from './_encoder'
import { Assert } from '../../assert'
import { Value } from '@sinclair/typebox/value'
import { Type } from '@sinclair/typebox'
Expand All @@ -10,15 +11,15 @@ describe('value/transform/BigInt', () => {
.Decode((value) => value)
.Encode((value) => value)
it('Should decode identity', () => {
const R = Value.Decode(T0, 5n)
const R = Encoder.Decode(T0, 5n)
Assert.IsEqual(R, 5n)
})
it('Should encode identity', () => {
const R = Value.Encode(T0, 5n)
const R = Encoder.Encode(T0, 5n)
Assert.IsEqual(R, 5n)
})
it('Should throw on identity decode', () => {
Assert.Throws(() => Value.Decode(T0, null))
Assert.Throws(() => Encoder.Decode(T0, null))
})
// --------------------------------------------------------
// Mapped
Expand All @@ -27,14 +28,14 @@ describe('value/transform/BigInt', () => {
.Decode((value) => 1)
.Encode((value) => 2n)
it('Should decode mapped', () => {
const R = Value.Decode(T1, 1n)
const R = Encoder.Decode(T1, 1n)
Assert.IsEqual(R, 1)
})
it('Should encode mapped', () => {
const R = Value.Encode(T1, null)
const R = Encoder.Encode(T1, null)
Assert.IsEqual(R, 2n)
})
it('Should throw on mapped decode', () => {
Assert.Throws(() => Value.Decode(T1, null))
Assert.Throws(() => Encoder.Decode(T1, null))
})
})
Loading

0 comments on commit 1d5cd5b

Please sign in to comment.