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 sized array rule constructor #1997

Merged
merged 2 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ Rulr provides rules that sanitize inputs from HTTP headers and URL params.
Finally, Rulr is starting to provide rule constructors that allow you quickly make your own rules.

- [regexRuleConstructor](./src/ruleConstructors/regexRuleConstructor/readme.md)
- [sizedArrayRuleConstructor](./src/ruleConstructors/sizedArrayRuleConstructor/readme.md)

### Frequently Awesome Questions 🤘

Expand Down
48 changes: 48 additions & 0 deletions src/ruleConstructors/sizedArrayRuleConstructor/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# sizedArrayRuleConstructor

[Back to root readme.md](../../../readme.md)

This function can be used to construct rules that ensure an array input has a size within a specified range and that each item in the array satisfies a given rule. The function also generates an error class. The rule should only throw errors with the generated error class or the errors from [the array rule](../../higherOrderRules/array/readme.md).

```ts
import * as rulr from 'rulr'

const exampleSymbol = Symbol()
const [exampleArray, ExampleArrayError, exampleArrayGuard] = rulr.sizedArrayRuleConstructor(
rulr.number,
1,
3,
exampleSymbol
)

const constrainToExample = rulr.object({
required: {
example: exampleArray,
},
})

type Example = rulr.Static<typeof constrainToExample>
// {
// example: rulr.Constrained<typeof exampleSymbol, number[]>
// }

// Valid
const example1: Example = constrainToExample({
example: [1, 2],
})

// Invalid: Array size is less than minSize
const example2: Example = constrainToExample({
example: [],
})

// Invalid: Array size is greater than maxSize
const example3: Example = constrainToExample({
example: [1, 2, 3, 4],
})

// Invalid: Array contains invalid item
const example4: Example = constrainToExample({
example: [1, '2'],
})
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import * as assert from 'assert'
import { sizedArrayRuleConstructor, Constrained, number, ValidationErrors, KeyedValidationError } from '../../rulr'

const ruleSymbol = Symbol()
const [rule, InvalidValueError] = sizedArrayRuleConstructor(number, 2, 3, ruleSymbol)

test('sizedArrayRuleConstructor rule should allow a valid array within size range', () => {
const input = [1, 2]
const output: Constrained<typeof ruleSymbol, unknown[]> = rule(input)
assert.deepStrictEqual(output, input)
assert.ok(InvalidValueError)
})

test('sizedArrayRuleConstructor rule should not allow an array smaller than minSize', () => {
const input = [1]
assert.throws(() => rule(input), InvalidValueError)
})

test('sizedArrayRuleConstructor rule should not allow an array larger than maxSize', () => {
const input = [1, 2, 3, 4]
assert.throws(() => rule(input), InvalidValueError)
})

test('sizedArrayRuleConstructor rule should not allow an array with invalid items', () => {
try {
rule([1, '2'])
assert.fail('Expected error')
} catch (error) {
if (error instanceof ValidationErrors) {
assert.strictEqual(error.errors.length, 1)
assert.ok(error.errors[0] instanceof KeyedValidationError)
return
} else {
assert.fail('Expected ValidationErrors')
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { BaseError } from 'make-error';
import { Constrained, Rule } from '../../core'
import { array } from '../../higherOrderRules/array/array'

type Result<T extends symbol> = [
Rule<Constrained<T, unknown[]>>,
typeof BaseError
]

export function sizedArrayRuleConstructor<Item, RuleSymbol extends symbol>(
itemRule: Rule<Item>,
minSize: number,
maxSize: number,
symbol: RuleSymbol,
): Result<RuleSymbol> {
type SizedArray = Constrained<typeof symbol, Item[]>;
const arrayRule = array(itemRule);

class InvalidArraySizeError extends BaseError {
constructor() {
super(`expected only ${minSize} to ${maxSize} items`)
}
}

function rule(input: unknown): SizedArray {
const arrayInput = arrayRule(input);
if (arrayInput.length >= minSize && arrayInput.length <= maxSize) {
return arrayInput as SizedArray;
}
throw new InvalidArraySizeError();
}

return [rule, InvalidArraySizeError]
}
1 change: 1 addition & 0 deletions src/rulr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export { object, InvalidObjectError, PlainObject } from './higherOrderRules/obje
export { tuple } from './higherOrderRules/tuple/tuple'
export { union, UnionValidationError } from './higherOrderRules/union/union'
export { regexRuleConstructor } from './ruleConstructors/regexRuleConstructor/regexRuleConstructor'
export { sizedArrayRuleConstructor } from './ruleConstructors/sizedArrayRuleConstructor/sizedArrayRuleConstructor'
export {
sanitizeBasicAuthFromString,
isBasicAuthAsString,
Expand Down
Loading