-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from vbudovski/feature/set-validator
Feature/set validator
- Loading branch information
Showing
8 changed files
with
426 additions
and
5 deletions.
There are no files selected for viewing
43 changes: 43 additions & 0 deletions
43
paseri-docs/src/content/docs/reference/Collections/set.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
--- | ||
title: "Set" | ||
sidebar: | ||
order: 25 | ||
--- | ||
|
||
```typescript | ||
import * as p from '@vbudovski/paseri'; | ||
|
||
const schema = p.set(p.number()); | ||
const data = new Set([1, 2, 3]); | ||
|
||
const result = schema.safeParse(data); | ||
if (result.ok) { | ||
// result.value typed as `Set<number>`. | ||
} | ||
``` | ||
|
||
## Validators | ||
|
||
### `min` | ||
|
||
Consists of at least `size` elements. | ||
|
||
```typescript | ||
p.set(p.number()).min(3); | ||
``` | ||
|
||
### `max` | ||
|
||
Consists of at most `size` elements. | ||
|
||
```typescript | ||
p.set(p.number()).max(3); | ||
``` | ||
|
||
### `length` | ||
|
||
Consists of exactly `size` elements. | ||
|
||
```typescript | ||
p.set(p.number()).size(3); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { z } from 'zod'; | ||
import * as p from '../../src/index.ts'; | ||
|
||
const { bench } = Deno; | ||
|
||
const paseriSchema = p.set(p.number()); | ||
const zodSchema = z.set(z.number()); | ||
|
||
const dataValid = new Set<number>([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]); | ||
const dataInvalid = null; | ||
|
||
bench('Paseri', { group: 'Type valid' }, () => { | ||
paseriSchema.safeParse(dataValid); | ||
}); | ||
|
||
bench('Zod', { group: 'Type valid' }, () => { | ||
zodSchema.safeParse(dataValid); | ||
}); | ||
|
||
bench('Paseri', { group: 'Type invalid' }, () => { | ||
paseriSchema.safeParse(dataInvalid); | ||
}); | ||
|
||
bench('Zod', { group: 'Type invalid' }, () => { | ||
zodSchema.safeParse(dataInvalid); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ export { | |
number, | ||
object, | ||
record, | ||
set, | ||
string, | ||
symbol, | ||
tuple, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
import { expect } from '@std/expect'; | ||
import { expectTypeOf } from 'expect-type'; | ||
import fc from 'fast-check'; | ||
import * as p from '../index.ts'; | ||
import type { TreeNode } from '../issue.ts'; | ||
|
||
const { test } = Deno; | ||
|
||
test('Valid type', () => { | ||
const schema = p.set(p.number()); | ||
|
||
fc.assert( | ||
fc.property(fc.array(fc.float()), (data) => { | ||
const dataAsSet = new Set(data); | ||
|
||
const result = schema.safeParse(dataAsSet); | ||
if (result.ok) { | ||
expectTypeOf(result.value).toEqualTypeOf<Set<number>>; | ||
expect(result.value).toEqual(dataAsSet); | ||
} else { | ||
expect(result.ok).toBeTruthy(); | ||
} | ||
}), | ||
); | ||
}); | ||
|
||
test('Invalid type', () => { | ||
const schema = p.set(p.number()); | ||
|
||
fc.assert( | ||
fc.property(fc.anything(), (data) => { | ||
const result = schema.safeParse(data); | ||
if (!result.ok) { | ||
expect(result.issue).toEqual({ type: 'leaf', code: 'invalid_type' }); | ||
} else { | ||
expect(result.ok).toBeFalsy(); | ||
} | ||
}), | ||
); | ||
}); | ||
|
||
test('Valid min', () => { | ||
const schema = p.set(p.number()).min(3); | ||
|
||
fc.assert( | ||
fc.property( | ||
fc.array(fc.float(), { minLength: 3 }).filter((value) => new Set(value).size >= 3), | ||
(data) => { | ||
const dataAsSet = new Set(data); | ||
|
||
const result = schema.safeParse(dataAsSet); | ||
if (result.ok) { | ||
expectTypeOf(result.value).toEqualTypeOf<Set<number>>; | ||
expect(result.value).toBe(dataAsSet); | ||
} else { | ||
expect(result.ok).toBeTruthy(); | ||
} | ||
}, | ||
), | ||
); | ||
}); | ||
|
||
test('Invalid min', () => { | ||
const schema = p.set(p.number()).min(3); | ||
|
||
fc.assert( | ||
fc.property( | ||
fc.array(fc.float(), { maxLength: 2 }).filter((value) => new Set(value).size <= 2), | ||
(data) => { | ||
const dataAsSet = new Set(data); | ||
|
||
const result = schema.safeParse(dataAsSet); | ||
if (!result.ok) { | ||
expect(result.issue).toEqual({ type: 'leaf', code: 'too_short' }); | ||
} else { | ||
expect(result.ok).toBeFalsy(); | ||
} | ||
}, | ||
), | ||
); | ||
}); | ||
|
||
test('Valid max', () => { | ||
const schema = p.set(p.number()).max(3); | ||
|
||
fc.assert( | ||
fc.property( | ||
fc.array(fc.float(), { maxLength: 3 }).filter((value) => new Set(value).size <= 3), | ||
(data) => { | ||
const dataAsSet = new Set(data); | ||
|
||
const result = schema.safeParse(dataAsSet); | ||
if (result.ok) { | ||
expectTypeOf(result.value).toEqualTypeOf<Set<number>>; | ||
expect(result.value).toBe(dataAsSet); | ||
} else { | ||
expect(result.ok).toBeTruthy(); | ||
} | ||
}, | ||
), | ||
); | ||
}); | ||
|
||
test('Invalid max', () => { | ||
const schema = p.set(p.number()).max(3); | ||
|
||
fc.assert( | ||
fc.property( | ||
fc.array(fc.float(), { minLength: 4 }).filter((value) => new Set(value).size >= 4), | ||
(data) => { | ||
const dataAsSet = new Set(data); | ||
|
||
const result = schema.safeParse(dataAsSet); | ||
if (!result.ok) { | ||
expect(result.issue).toEqual({ type: 'leaf', code: 'too_long' }); | ||
} else { | ||
expect(result.ok).toBeFalsy(); | ||
} | ||
}, | ||
), | ||
); | ||
}); | ||
|
||
test('Valid size', () => { | ||
const schema = p.set(p.number()).size(3); | ||
|
||
fc.assert( | ||
fc.property( | ||
fc.array(fc.float(), { minLength: 3, maxLength: 3 }).filter((value) => new Set(value).size === 3), | ||
(data) => { | ||
const dataAsSet = new Set(data); | ||
|
||
const result = schema.safeParse(dataAsSet); | ||
if (result.ok) { | ||
expectTypeOf(result.value).toEqualTypeOf<Set<number>>; | ||
expect(result.value).toBe(dataAsSet); | ||
} else { | ||
expect(result.ok).toBeTruthy(); | ||
} | ||
}, | ||
), | ||
); | ||
}); | ||
|
||
test('Invalid size (too long)', () => { | ||
const schema = p.set(p.number()).size(3); | ||
|
||
fc.assert( | ||
fc.property( | ||
fc.array(fc.float(), { minLength: 4 }).filter((value) => new Set(value).size >= 4), | ||
(data) => { | ||
const dataAsSet = new Set(data); | ||
|
||
const result = schema.safeParse(dataAsSet); | ||
if (!result.ok) { | ||
expect(result.issue).toEqual({ type: 'leaf', code: 'too_long' }); | ||
} else { | ||
expect(result.ok).toBeFalsy(); | ||
} | ||
}, | ||
), | ||
); | ||
}); | ||
|
||
test('Invalid size (too short)', () => { | ||
const schema = p.set(p.number()).size(3); | ||
|
||
fc.assert( | ||
fc.property( | ||
fc.array(fc.float(), { maxLength: 2 }).filter((value) => new Set(value).size <= 2), | ||
(data) => { | ||
const dataAsSet = new Set(data); | ||
|
||
const result = schema.safeParse(dataAsSet); | ||
if (!result.ok) { | ||
expect(result.issue).toEqual({ type: 'leaf', code: 'too_short' }); | ||
} else { | ||
expect(result.ok).toBeFalsy(); | ||
} | ||
}, | ||
), | ||
); | ||
}); | ||
|
||
test('Invalid elements', () => { | ||
const schema = p.set(p.number()); | ||
const data = new Set([1, 'foo', 2, 'bar']); | ||
|
||
const result = schema.safeParse(data); | ||
if (!result.ok) { | ||
const expectedResult: TreeNode = { | ||
type: 'join', | ||
left: { type: 'nest', key: 1, child: { type: 'leaf', code: 'invalid_type' } }, | ||
right: { type: 'nest', key: 3, child: { type: 'leaf', code: 'invalid_type' } }, | ||
}; | ||
expect(result.issue).toEqual(expectedResult); | ||
} else { | ||
expect(result.ok).toBeFalsy(); | ||
} | ||
}); | ||
|
||
test('Optional', () => { | ||
const schema = p.set(p.number()).optional(); | ||
|
||
fc.assert( | ||
fc.property(fc.option(fc.array(fc.float()), { nil: undefined }), (data) => { | ||
const dataAsSet = new Set(data); | ||
|
||
const result = schema.safeParse(dataAsSet); | ||
if (result.ok) { | ||
expectTypeOf(result.value).toEqualTypeOf<Set<number> | undefined>; | ||
expect(result.value).toEqual(dataAsSet); | ||
} else { | ||
expect(result.ok).toBeTruthy(); | ||
} | ||
}), | ||
); | ||
}); | ||
|
||
test('Nullable', () => { | ||
const schema = p.set(p.number()).nullable(); | ||
|
||
fc.assert( | ||
fc.property(fc.option(fc.array(fc.float()), { nil: null }), (data) => { | ||
const dataAsSet = new Set(data); | ||
|
||
const result = schema.safeParse(dataAsSet); | ||
if (result.ok) { | ||
expectTypeOf(result.value).toEqualTypeOf<Set<number> | null>; | ||
expect(result.value).toEqual(dataAsSet); | ||
} else { | ||
expect(result.ok).toBeTruthy(); | ||
} | ||
}), | ||
); | ||
}); | ||
|
||
test('Immutable', async (t) => { | ||
await t.step('min', () => { | ||
const original = p.set(p.string()); | ||
const modified = original.min(3); | ||
expect(modified).not.toEqual(original); | ||
}); | ||
|
||
await t.step('max', () => { | ||
const original = p.set(p.string()); | ||
const modified = original.max(3); | ||
expect(modified).not.toEqual(original); | ||
}); | ||
|
||
await t.step('size', () => { | ||
const original = p.set(p.string()); | ||
const modified = original.size(3); | ||
expect(modified).not.toEqual(original); | ||
}); | ||
}); |
Oops, something went wrong.