Skip to content

Commit dbdbf6b

Browse files
committed
feat: add modifier structure
1 parent de19fc4 commit dbdbf6b

8 files changed

Lines changed: 82 additions & 37 deletions

File tree

src/permutation/definitions.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
export type Permutation<T = unknown> = {
22
readonly size: number;
33
readonly type: string;
4+
/**
5+
* Modifiers are like meta-data that are used only by consumers
6+
* All modifiers should be idempotent
7+
*/
8+
readonly modifiers: string[];
49
} & Iterable<T>;
510
export type UnwrapPermutation<T extends Permutation> = T extends Permutation<infer U> ? U : never;
611
export type PermutationGenerator<T extends Permutation = Permutation> = () => T;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from '#src/permutation/modifiers/optional';
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type {
2+
Permutation,
3+
PermutationGenerator,
4+
UnwrapPermutation,
5+
} from '#src/permutation/definitions';
6+
7+
export function optional<const T extends Permutation>(input: PermutationGenerator<T>) {
8+
return function () {
9+
const r = input();
10+
if (r.modifiers.includes('optional')) return r;
11+
return { ...r, modifiers: ['optional', ...r.modifiers] };
12+
} as PermutationGenerator<
13+
'optional' extends keyof T['modifiers']
14+
? T
15+
: Iterable<UnwrapPermutation<T>> & {
16+
readonly size: T['size'];
17+
readonly type: T['type'];
18+
readonly modifiers: ['optional', ...T['modifiers']];
19+
}
20+
>;
21+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type {
2+
Permutation,
3+
PermutationGenerator,
4+
UnwrapPermutation,
5+
} from '#src/permutation/definitions';
6+
import type { Sum } from '#src/utils/arithmetic/sum';
7+
import type { Length } from '#src/utils/common';
8+
9+
export function concat<const T extends Permutation, const U extends unknown[]>(
10+
input: PermutationGenerator<T>,
11+
...values: U
12+
) {
13+
return function () {
14+
const r = input();
15+
return {
16+
size: (r.size + values.length) as Sum<T['size'], Length<U>>,
17+
type: r.type,
18+
modifiers: r.modifiers,
19+
*[Symbol.iterator]() {
20+
yield* r;
21+
yield* values;
22+
},
23+
};
24+
} as PermutationGenerator<
25+
{
26+
readonly size: Sum<T['size'], Length<U>>;
27+
readonly type: T['type'];
28+
readonly modifiers: T['modifiers'];
29+
} & Iterable<UnwrapPermutation<T> | U[number]>
30+
>;
31+
}

src/permutation/primitive/each.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ export function each<const T extends readonly unknown[]>(...values: T) {
66
return {
77
size: values.length as Length<T>,
88
type: 'each',
9+
modifiers: [],
910
*[Symbol.iterator]() {
1011
yield* values;
1112
},
1213
};
1314
} as PermutationGenerator<
14-
{
15-
readonly size: Length<T>;
16-
readonly type: 'each';
17-
} & Iterable<TupleToUnion<T>>
15+
{ readonly size: Length<T>; readonly type: 'each'; readonly modifiers: [] } & Iterable<
16+
TupleToUnion<T>
17+
>
1818
>;
1919
}
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
export * from '#src/permutation/primitive/each';
2-
export * from '#src/permutation/primitive/optional';
32
export * from '#src/permutation/primitive/record';

src/permutation/primitive/optional.ts

Lines changed: 0 additions & 26 deletions
This file was deleted.

src/permutation/primitive/record.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import type {
2+
Permutation,
23
PermutationGenerator,
34
UnwrapPermutation,
45
UnwrapPermutationGenerator,
56
} from '#src/permutation/definitions';
7+
import { concat } from '#src/permutation/primitive/concat';
68
import { explicitPermutations } from '#src/permutation/pure/explicit-permutations';
9+
import { REMOVE } from '#src/permutation/symbols';
710
import type { PlainType } from '#src/utils/common';
811
import { isExpandableArray } from '#src/utils/expandable-check';
912

@@ -19,11 +22,23 @@ export type RecordGenerator<T extends ValidRecordInput> = PlainType<{
1922

2023
export function record<const T extends ValidRecordInput>(input: T) {
2124
return function () {
22-
const r = Object.entries(input).map(([k, v]) => [k, v()] as const);
23-
const size = r.reduce((acc, curr) => acc * curr[1].size, 1);
25+
const r = Object.entries(input).map(([k, v]) => {
26+
const b = v();
27+
const ret = b.modifiers.includes('optional')
28+
? ([k, concat(v, REMOVE)()] as const)
29+
: ([k, b] as const);
30+
return ret as [string, Permutation];
31+
});
32+
const size = r.reduce((acc, curr) => {
33+
const [, p] = curr;
34+
const isOptional = p.modifiers.includes('optional');
35+
const currentSize = p.size + (isOptional ? 1 : 0);
36+
return acc * currentSize;
37+
}, 1);
2438
return {
2539
size,
2640
type: 'record',
41+
modifiers: [],
2742
*[Symbol.iterator]() {
2843
if (isExpandableArray(input)) {
2944
yield* explicitPermutations(r.map((v) => v[1]));
@@ -34,9 +49,8 @@ export function record<const T extends ValidRecordInput>(input: T) {
3449
},
3550
};
3651
} as PermutationGenerator<
37-
{
38-
readonly size: number;
39-
readonly type: 'record';
40-
} & Iterable<RecordGenerator<T>>
52+
{ readonly size: number; readonly type: 'record'; readonly modifiers: [] } & Iterable<
53+
RecordGenerator<T>
54+
>
4155
>;
4256
}

0 commit comments

Comments
 (0)