Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add Typed Array #78

Merged
merged 42 commits into from
Apr 29, 2022
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
7939887
feat: add typed array
imranbarbhuiya Mar 21, 2022
2e26c66
fix: use node:util/types
imranbarbhuiya Mar 21, 2022
1b5bf84
fix: replace nodejs.typearray from shapes
imranbarbhuiya Mar 21, 2022
9989bb7
chore: remove unnc file
imranbarbhuiya Mar 21, 2022
3bf5962
test: add tests
imranbarbhuiya Mar 21, 2022
ca5fa8f
chore: update readme
imranbarbhuiya Mar 23, 2022
0f904d2
fix: make type readonly
imranbarbhuiya Mar 23, 2022
e63ff7a
fix: make browser compitible
imranbarbhuiya Mar 23, 2022
1374474
fix: remove useless assignment
imranbarbhuiya Mar 23, 2022
afa775f
Apply suggestions from code review
imranbarbhuiya Apr 20, 2022
36b5457
chore: readme codeblock fix
imranbarbhuiya Apr 20, 2022
ae98993
fix: shorten name
imranbarbhuiya Apr 24, 2022
67afc13
fix(test): tests fix
imranbarbhuiya Apr 24, 2022
e839307
fix(types): types/node is installed
imranbarbhuiya Apr 24, 2022
131b3c5
fix(test): update all tests
imranbarbhuiya Apr 24, 2022
ec59e29
fix: redefine typedarray
imranbarbhuiya Apr 24, 2022
f63afa8
fix: change as suggested
imranbarbhuiya Apr 26, 2022
0a71f80
fix: update readme
imranbarbhuiya Apr 26, 2022
990fa35
fix: update message
imranbarbhuiya Apr 26, 2022
a8072f6
revert: fix: shorten name
imranbarbhuiya Apr 28, 2022
7823f3e
feat: add typed array
imranbarbhuiya Mar 21, 2022
854cdf2
fix: use node:util/types
imranbarbhuiya Mar 21, 2022
ef139e7
fix: replace nodejs.typearray from shapes
imranbarbhuiya Mar 21, 2022
7584014
chore: remove unnc file
imranbarbhuiya Mar 21, 2022
586895f
test: add tests
imranbarbhuiya Mar 21, 2022
4c5d397
chore: update readme
imranbarbhuiya Mar 23, 2022
89dacf7
fix: make type readonly
imranbarbhuiya Mar 23, 2022
3684b62
fix: make browser compitible
imranbarbhuiya Mar 23, 2022
198928d
fix: remove useless assignment
imranbarbhuiya Mar 23, 2022
38f95af
Apply suggestions from code review
imranbarbhuiya Apr 20, 2022
edbeae6
chore: readme codeblock fix
imranbarbhuiya Apr 20, 2022
6c94751
fix: shorten name
imranbarbhuiya Apr 24, 2022
596f609
fix(test): tests fix
imranbarbhuiya Apr 24, 2022
7297ceb
fix(types): types/node is installed
imranbarbhuiya Apr 24, 2022
c64219d
fix(test): update all tests
imranbarbhuiya Apr 24, 2022
046ac20
fix: redefine typedarray
imranbarbhuiya Apr 24, 2022
354aab3
fix: change as suggested
imranbarbhuiya Apr 26, 2022
1f2f7a8
fix: update readme
imranbarbhuiya Apr 26, 2022
2c032c7
fix: update message
imranbarbhuiya Apr 26, 2022
c471504
revert: fix: shorten name
imranbarbhuiya Apr 28, 2022
3391381
fix: readme fix
imranbarbhuiya Apr 29, 2022
fb60d06
fix: conflict
imranbarbhuiya Apr 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 37 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,12 +282,10 @@ const noDependencies = pkg.omit(['dependencies']);
Inspired by TypeScript's built-in `Partial` utility type, all object schemas have the aforementioned method that makes all properties optional:

```typescript
const user = s
.object({
username: s.string,
password: s.string
})
.partial;
const user = s.object({
username: s.string,
password: s.string
}).partial;
```

Which is the same as doing:
Expand Down Expand Up @@ -423,6 +421,39 @@ s.function([s.string, s.number], s.string); // (arg0: string, arg1: number) => s

---

#### TypedArray

```ts
const typedArray = s.typedArray();
const i8Array = s.i8Array;
const u8Array = s.u8Array;
const u8clampedArray = s.u8clampedArray;
const i16Array = s.i16Array;
const u16Array = s.u16Array;
const i32Array = s.i32Array;
const u32Array = s.u32Array;
const f32Array = s.f32Array;
const f64Array = s.f64Array;
const i64Array = s.i64Array;
const u64Array = s.u64Array;
```

ShapeShift includes a handful of validations specific to typed arrays.

```typescript
s.typedArray().lengthLt(5); // Length must be less than 5
s.typedArray().lengthLe(5); // Length must be 5 or less
s.typedArray().lengthGt(5); // Length must be more than 5
s.typedArray().lengthGe(5); // Length must be 5 or more
s.typedArray().lengthEq(5); // Length must be exactly 5
s.typedArray().lengthNe(5); // Length must not be 5
s.typedArray().lengthRange(0, 4); // Length L must satisfy 0 <= L < 4
s.typedArray().lengthRangeInclusive(0, 4); // Length L must satisfy 0 <= L <= 4
s.typedArray().lengthRangeExclusive(0, 4); // Length L must satisfy 0 < L < 4
```

Note that all of these methods have analogous methods for working with the typed array's byte length, `s.typedArray().byteLengthX()` - for instance, `s.typedArray().byteLengthLt(5)` is the same as `s.typedArray().lengthLt(5)` but for the array's byte length.

### BaseValidator: methods and properties

All schemas in ShapeShift contain certain methods.
Expand Down
176 changes: 176 additions & 0 deletions src/constraints/TypedArrayLengthConstraints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { ExpectedConstraintError } from '../lib/errors/ExpectedConstraintError';
import { Result } from '../lib/Result';
import type { IConstraint } from './base/IConstraint';
import { Comparator, eq, ge, gt, le, lt, ne } from './util/operators';
import type { TypedArray } from './util/typedArray';

export type TypedArrayConstraintName = `s.typedArray(T).${'byteLength' | 'length'}${
| 'Lt'
| 'Le'
| 'Gt'
| 'Ge'
| 'Eq'
| 'Ne'
| 'Range'
| 'RangeInclusive'
| 'RangeExclusive'}`;

function typedArrayByteLengthComparator<T extends TypedArray>(
comparator: Comparator,
name: TypedArrayConstraintName,
expected: string,
length: number
): IConstraint<T> {
return {
run(input: T) {
return comparator(input.byteLength, length) //
? Result.ok(input)
: Result.err(new ExpectedConstraintError(name, 'Invalid Typed Array byte length', input, expected));
}
};
}

export function typedArrayByteLengthLt<T extends TypedArray>(value: number): IConstraint<T> {
const expected = `expected.byteLength < ${value}`;
return typedArrayByteLengthComparator(lt, 's.typedArray(T).byteLengthLt', expected, value);
}

export function typedArrayByteLengthLe<T extends TypedArray>(value: number): IConstraint<T> {
const expected = `expected.byteLength <= ${value}`;
return typedArrayByteLengthComparator(le, 's.typedArray(T).byteLengthLe', expected, value);
}

export function typedArrayByteLengthGt<T extends TypedArray>(value: number): IConstraint<T> {
const expected = `expected.byteLength > ${value}`;
return typedArrayByteLengthComparator(gt, 's.typedArray(T).byteLengthGt', expected, value);
}

export function typedArrayByteLengthGe<T extends TypedArray>(value: number): IConstraint<T> {
const expected = `expected.byteLength >= ${value}`;
return typedArrayByteLengthComparator(ge, 's.typedArray(T).byteLengthGe', expected, value);
}

export function typedArrayByteLengthEq<T extends TypedArray>(value: number): IConstraint<T> {
const expected = `expected.byteLength === ${value}`;
return typedArrayByteLengthComparator(eq, 's.typedArray(T).byteLengthEq', expected, value);
}

export function typedArrayByteLengthNe<T extends TypedArray>(value: number): IConstraint<T> {
const expected = `expected.byteLength !== ${value}`;
return typedArrayByteLengthComparator(ne, 's.typedArray(T).byteLengthNe', expected, value);
}

export function typedArrayByteLengthRange<T extends TypedArray>(start: number, endBefore: number): IConstraint<T> {
const expected = `expected.byteLength >= ${start} && expected.byteLength < ${endBefore}`;
return {
run(input: T) {
return input.byteLength >= start && input.byteLength < endBefore //
? Result.ok(input)
: Result.err(new ExpectedConstraintError('s.typedArray(T).byteLengthRange', 'Invalid Typed Array byte length', input, expected));
}
};
}

export function typedArrayByteLengthRangeInclusive<T extends TypedArray>(start: number, end: number) {
const expected = `expected.byteLength >= ${start} && expected.byteLength <= ${end}`;
return {
run(input: T) {
return input.byteLength >= start && input.byteLength <= end //
? Result.ok(input)
: Result.err(
new ExpectedConstraintError('s.typedArray(T).byteLengthRangeInclusive', 'Invalid Typed Array byte length', input, expected)
);
}
};
}

export function typedArrayByteLengthRangeExclusive<T extends TypedArray>(startAfter: number, endBefore: number): IConstraint<T> {
const expected = `expected.byteLength > ${startAfter} && expected.byteLength < ${endBefore}`;
return {
run(input: T) {
return input.byteLength > startAfter && input.byteLength < endBefore //
? Result.ok(input)
: Result.err(
new ExpectedConstraintError('s.typedArray(T).byteLengthRangeExclusive', 'Invalid Typed Array byte length', input, expected)
);
}
};
}

function typedArrayLengthComparator<T extends TypedArray>(
comparator: Comparator,
name: TypedArrayConstraintName,
expected: string,
length: number
): IConstraint<T> {
return {
run(input: T) {
return comparator(input.length, length) //
? Result.ok(input)
: Result.err(new ExpectedConstraintError(name, 'Invalid Typed Array length', input, expected));
}
};
}

export function typedArrayLengthLt<T extends TypedArray>(value: number): IConstraint<T> {
const expected = `expected.length < ${value}`;
return typedArrayLengthComparator(lt, 's.typedArray(T).lengthLt', expected, value);
}

export function typedArrayLengthLe<T extends TypedArray>(value: number): IConstraint<T> {
const expected = `expected.length <= ${value}`;
return typedArrayLengthComparator(le, 's.typedArray(T).lengthLe', expected, value);
}

export function typedArrayLengthGt<T extends TypedArray>(value: number): IConstraint<T> {
const expected = `expected.length > ${value}`;
return typedArrayLengthComparator(gt, 's.typedArray(T).lengthGt', expected, value);
}

export function typedArrayLengthGe<T extends TypedArray>(value: number): IConstraint<T> {
const expected = `expected.length >= ${value}`;
return typedArrayLengthComparator(ge, 's.typedArray(T).lengthGe', expected, value);
}

export function typedArrayLengthEq<T extends TypedArray>(value: number): IConstraint<T> {
const expected = `expected.length === ${value}`;
return typedArrayLengthComparator(eq, 's.typedArray(T).lengthEq', expected, value);
}

export function typedArrayLengthNe<T extends TypedArray>(value: number): IConstraint<T> {
const expected = `expected.length !== ${value}`;
return typedArrayLengthComparator(ne, 's.typedArray(T).lengthNe', expected, value);
}

export function typedArrayLengthRange<T extends TypedArray>(start: number, endBefore: number): IConstraint<T> {
const expected = `expected.length >= ${start} && expected.length < ${endBefore}`;
return {
run(input: T) {
return input.length >= start && input.length < endBefore //
? Result.ok(input)
: Result.err(new ExpectedConstraintError('s.typedArray(T).lengthRange', 'Invalid Typed Array length', input, expected));
}
};
}

export function typedArrayLengthRangeInclusive<T extends TypedArray>(start: number, end: number): IConstraint<T> {
const expected = `expected.length >= ${start} && expected.length <= ${end}`;
return {
run(input: T) {
return input.length >= start && input.length <= end //
? Result.ok(input)
: Result.err(new ExpectedConstraintError('s.typedArray(T).lengthRangeInclusive', 'Invalid Typed Array length', input, expected));
}
};
}

export function typedArrayLengthRangeExclusive<T extends TypedArray>(startAfter: number, endBefore: number): IConstraint<T> {
const expected = `expected.length > ${startAfter} && expected.length < ${endBefore}`;
return {
run(input: T) {
return input.length > startAfter && input.length < endBefore //
? Result.ok(input)
: Result.err(new ExpectedConstraintError('s.typedArray(T).lengthRangeExclusive', 'Invalid Typed Array length', input, expected));
}
};
}
21 changes: 21 additions & 0 deletions src/constraints/type-exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,27 @@ export type {
arrayLengthRangeExclusive,
arrayLengthRangeInclusive
} from './ArrayLengthConstraints';
export type {
TypedArrayConstraintName,
typedArrayByteLengthEq,
typedArrayByteLengthGe,
typedArrayByteLengthGt,
typedArrayByteLengthLe,
typedArrayByteLengthLt,
typedArrayByteLengthNe,
typedArrayByteLengthRange,
typedArrayByteLengthRangeExclusive,
typedArrayByteLengthRangeInclusive,
typedArrayLengthEq,
typedArrayLengthGe,
typedArrayLengthGt,
typedArrayLengthLe,
typedArrayLengthLt,
typedArrayLengthNe,
typedArrayLengthRange,
typedArrayLengthRangeExclusive,
typedArrayLengthRangeInclusive
} from './TypedArrayLengthConstraints';
export type { IConstraint } from './base/IConstraint';
export type { BigIntConstraintName, bigintDivisibleBy, bigintEq, bigintGe, bigintGt, bigintLe, bigintLt, bigintNe } from './BigIntConstraints';
export type { BooleanConstraintName, booleanFalse, booleanTrue } from './BooleanConstraints';
Expand Down
29 changes: 29 additions & 0 deletions src/constraints/util/typedArray.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export type TypedArray =
| Int8Array
| Uint8Array
| Uint8ClampedArray
| Int16Array
| Uint16Array
| Int32Array
| Uint32Array
| Float32Array
| Float64Array
| BigInt64Array
| BigUint64Array;

export const TypedArrays = {
i8Array: (x: unknown): x is Int8Array => x instanceof Int8Array,
u8Array: (x: unknown): x is Uint8Array => x instanceof Uint8Array,
u8clampedArray: (x: unknown): x is Uint8ClampedArray => x instanceof Uint8ClampedArray,
i16Array: (x: unknown): x is Int16Array => x instanceof Int16Array,
u16Array: (x: unknown): x is Uint16Array => x instanceof Uint16Array,
i32Array: (x: unknown): x is Int32Array => x instanceof Int32Array,
u32Array: (x: unknown): x is Uint32Array => x instanceof Uint32Array,
f32Array: (x: unknown): x is Float32Array => x instanceof Float32Array,
f64Array: (x: unknown): x is Float64Array => x instanceof Float64Array,
i64Array: (x: unknown): x is BigInt64Array => x instanceof BigInt64Array,
u64Array: (x: unknown): x is BigUint64Array => x instanceof BigUint64Array,
TypedArray: (x: unknown): x is TypedArray => ArrayBuffer.isView(x) && !(x instanceof DataView)
} as const;

export type TypedArrayName = keyof typeof TypedArrays;
50 changes: 50 additions & 0 deletions src/lib/Shapes.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { TypedArray, TypedArrayName } from '../constraints/util/typedArray';
import {
ArrayValidator,
BaseValidator,
Expand All @@ -19,6 +20,7 @@ import {
UnionValidator
} from '../validators/imports';
import { NativeEnumLike, NativeEnumValidator } from '../validators/NativeEnumValidator';
import { TypedArrayValidator } from '../validators/TypedArrayValidator';
import type { Constructor, MappedObjectValidator } from './util-types';

export class Shapes {
Expand Down Expand Up @@ -95,6 +97,54 @@ export class Shapes {
return new ArrayValidator(validator);
}

public typedArray<T extends TypedArray>(type: TypedArrayName = 'TypedArray') {
return new TypedArrayValidator<T>(type);
}

public get i8Array() {
return this.typedArray<Int8Array>('i8Array');
}

public get u8Array() {
return this.typedArray<Uint8Array>('u8Array');
}

public get u8clampedArray() {
return this.typedArray<Uint8ClampedArray>('u8clampedArray');
}

public get i16Array() {
return this.typedArray<Int16Array>('i16Array');
}

public get u16Array() {
return this.typedArray<Uint16Array>('u16Array');
}

public get i32Array() {
return this.typedArray<Int32Array>('i32Array');
}

public get u32Array() {
return this.typedArray<Uint32Array>('u32Array');
}

public get f32Array() {
return this.typedArray<Float32Array>('f32Array');
}

public get f64Array() {
return this.typedArray<Float64Array>('f64Array');
}

public get i64Array() {
return this.typedArray<BigInt64Array>('i64Array');
}

public get u64Array() {
return this.typedArray<BigUint64Array>('u64Array');
}

public tuple<T extends [...BaseValidator<any>[]]>(validators: [...T]): TupleValidator<UnwrapTuple<T>> {
return new TupleValidator(validators);
}
Expand Down
4 changes: 3 additions & 1 deletion src/lib/errors/BaseConstraintError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import type {
BooleanConstraintName,
DateConstraintName,
NumberConstraintName,
StringConstraintName
StringConstraintName,
TypedArrayConstraintName
} from '../../constraints/type-exports';
import { BaseError } from './BaseError';

export type ConstraintErrorNames =
| TypedArrayConstraintName
| ArrayConstraintName
| BigIntConstraintName
| BooleanConstraintName
Expand Down
Loading