-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathxor.ts
132 lines (123 loc) · 3.5 KB
/
xor.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import {
InferOutputTypes,
InferTypes,
ParseResult,
Schema,
ValidationOptions,
} from "../schema";
import { Unionize } from "../utility";
import { ValidationIssue, ValidationResult, isSuccess } from "../validation";
export function xor<T extends readonly Schema<unknown>[]>(
...schemas: T
): Schema<
Unionize<InferTypes<T>>,
Unionize<InferOutputTypes<T>>,
{ type: "xor"; schemas: T }
> {
return xorWithIssue(schemas);
}
export function xorWithIssue<T extends readonly Schema<unknown>[]>(
schemas: T,
issue?: string
): Schema<
Unionize<InferTypes<T>>,
Unionize<InferOutputTypes<T>>,
{ type: "xor"; schemas: T }
> {
type ResultI = Unionize<InferTypes<T>>;
type ResultO = Unionize<InferOutputTypes<T>>;
type V = ValidationResult<ResultI>;
class Aggregator {
constructor(readonly options: Partial<ValidationOptions> | undefined) {}
public lastFailure: ValidationResult<ResultI>;
private xorValidation: V;
private successSchema: Schema<unknown> | undefined;
onValidate(
v: unknown,
schema: Schema<unknown>,
validation: ValidationResult<unknown>
): boolean {
if (isSuccess(validation)) {
if (this.successSchema) {
this.xorValidation = new ValidationIssue("xor", issue, v) as V;
this.successSchema = undefined;
return true;
} else {
this.successSchema = schema;
}
} else {
this.lastFailure = validation as ValidationResult<ResultI>;
}
return false;
}
result(): V {
if (!this.successSchema) {
return this.xorValidation ?? this.lastFailure;
}
}
parseResult(v: unknown): ParseResult<ResultI, ResultO> {
if (this.successSchema === undefined) {
return { validation: this.xorValidation ?? this.lastFailure };
}
return this.successSchema.parse(v, {
...this.options,
skipValidation: true,
}) as ParseResult<ResultI, ResultO>;
}
}
const validate: Schema<ResultI, ResultO, unknown>["validate"] = (v, o) => {
const aggregator = new Aggregator(o);
for (const schema of schemas) {
const validation = schema.validate(v, o);
if (aggregator.onValidate(v, schema, validation)) {
break;
}
}
return aggregator.result();
};
const validateAsync: Schema<
ResultI,
ResultO,
unknown
>["validateAsync"] = async (v, o) => {
const aggregator = new Aggregator(o);
for (const schema of schemas) {
const validation = await schema.validateAsync(v, o);
if (aggregator.onValidate(v, schema, validation)) {
break;
}
}
return aggregator.result();
};
const parse: Schema<ResultI, ResultO, unknown>["parse"] = (v, o) => {
const aggregator = new Aggregator(o);
for (const schema of schemas) {
const validation = schema.validate(v, o);
if (aggregator.onValidate(v, schema, validation)) {
break;
}
}
return aggregator.parseResult(v);
};
const parseAsync: Schema<ResultI, ResultO, unknown>["parseAsync"] = async (
v,
o
) => {
const aggregator = new Aggregator(o);
for (const schema of schemas) {
const validation = await schema.validateAsync(v, o);
if (aggregator.onValidate(v, schema, validation)) {
break;
}
}
return aggregator.parseResult(v);
};
return {
accepts: (v, o): v is Unionize<InferTypes<T>> => isSuccess(validate(v, o)),
parse,
parseAsync,
validate,
validateAsync,
meta: () => ({ type: "xor", schemas }),
};
}