Skip to content

Commit 59e8caf

Browse files
committed
Fix refinement on union schema which also uses S.to
1 parent eb0367c commit 59e8caf

5 files changed

Lines changed: 433 additions & 320 deletions

File tree

IDEAS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- Fix https://github.com/DZakh/sury/issues/150
77
- Add `S.brand` for TS API
88
- Update Standard Schema error message to only include reason part
9+
- Fix refinement on union schema which also uses `S.to`
910

1011
## v11
1112

packages/sury/src/Sury.res

Lines changed: 50 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,9 @@ and internal = {
425425
mutable serializer?: builder,
426426
// Builder refine that the value matches the schema
427427
// Applies for both parsing and serializing
428-
mutable refiner?: builder,
428+
mutable refiner?: refinerBuilder,
429+
// Compiler for custom schema logic
430+
mutable compiler?: builder,
429431
// A schema we transform to
430432
mutable to?: internal,
431433
mutable const?: char, // use char to avoid Caml_option.some
@@ -507,6 +509,7 @@ and has = {
507509
object?: bool,
508510
}
509511
and builder = (b, ~input: val, ~selfSchema: internal, ~path: Path.t) => val
512+
and refinerBuilder = (b, ~inputVar: string, ~selfSchema: internal, ~path: Path.t) => string
510513
and val = {
511514
@as("b")
512515
mutable b: b,
@@ -1897,14 +1900,19 @@ let rec parse = (prevB: b, ~schema, ~input as inputArg: val, ~path) => {
18971900
b->B.unsupportedTransform(~from=input.contents->Obj.magic, ~target=schema, ~path)
18981901
}
18991902

1900-
switch schema.refiner {
1901-
| Some(refiner) =>
1902-
// Some refiners like union might return a new
1903-
// instance of value. This is used for an assumption
1904-
// that it's transformed.
1905-
input := refiner(b, ~input=input.contents, ~selfSchema=schema, ~path)
1906-
| _ => ()
1903+
switch schema.compiler {
1904+
| Some(compiler) => input := compiler(b, ~input=input.contents, ~selfSchema=schema, ~path)
1905+
| None => ()
1906+
}
1907+
1908+
if input.contents.skipTo !== Some(true) {
1909+
switch schema.refiner {
1910+
| Some(refiner) =>
1911+
b.code = b.code ++ refiner(b, ~inputVar=input.contents.var(b), ~selfSchema=schema, ~path)
1912+
| None => ()
1913+
}
19071914
}
1915+
19081916
switch schema.to {
19091917
| Some(to) =>
19101918
switch schema.parser {
@@ -2504,26 +2512,20 @@ let noValidation = (schema, value) => {
25042512
mut->castToPublic
25052513
}
25062514

2515+
let appendRefiner = (maybeExistingRefiner, refiner) => {
2516+
switch maybeExistingRefiner {
2517+
| Some(existingRefiner) =>
2518+
(b, ~inputVar, ~selfSchema, ~path) => {
2519+
existingRefiner(b, ~inputVar, ~selfSchema, ~path) ++ refiner(b, ~inputVar, ~selfSchema, ~path)
2520+
}
2521+
| None => refiner
2522+
}
2523+
}
2524+
25072525
let internalRefine = (schema, refiner) => {
25082526
let schema = schema->castToInternal
25092527
updateOutput(schema, mut => {
2510-
let prevRefiner = mut.refiner
2511-
mut.refiner = Some(
2512-
Builder.make((b, ~input, ~selfSchema, ~path) => {
2513-
// FIXME: Should it be applied in more places?
2514-
b->B.transform(
2515-
~input=switch prevRefiner {
2516-
| Some(prevRefiner) => prevRefiner(b, ~input, ~selfSchema, ~path)
2517-
| None => input
2518-
},
2519-
(b, ~input) => {
2520-
let rCode = refiner(b, ~inputVar=b->B.Val.var(input), ~selfSchema, ~path)
2521-
b.code = b.code ++ rCode
2522-
input
2523-
},
2524-
)
2525-
}),
2526-
)
2528+
mut.refiner = Some(appendRefiner(mut.refiner, refiner))
25272529
})
25282530
}
25292531

@@ -2614,7 +2616,7 @@ let neverBuilder = Builder.make((b, ~input, ~selfSchema, ~path) => {
26142616
})
26152617

26162618
let never = base(Never)
2617-
never.refiner = Some(neverBuilder)
2619+
never.compiler = Some(neverBuilder)
26182620
let never: t<never> = never->castToPublic
26192621

26202622
let nestedLoc = "BS_PRIVATE_NESTED_SOME_NONE"
@@ -2685,7 +2687,7 @@ module Union = {
26852687
})
26862688
}
26872689

2688-
let refiner = Builder.make((b, ~input, ~selfSchema, ~path) => {
2690+
let compiler = Builder.make((b, ~input, ~selfSchema, ~path) => {
26892691
let schemas = selfSchema.anyOf->X.Option.getUnsafe
26902692

26912693
switch input.anyOf {
@@ -2731,6 +2733,10 @@ module Union = {
27312733
let schema = switch selfSchema.to {
27322734
| Some(target) if !(selfSchema.parser->Obj.magic) && target.tag !== Union =>
27332735
updateOutput(schemas->Js.Array2.unsafe_get(idx), mut => {
2736+
switch selfSchema.refiner {
2737+
| Some(refiner) => mut.refiner = Some(appendRefiner(mut.refiner, refiner))
2738+
| None => ()
2739+
}
27342740
mut.to = Some(target)
27352741
})->castToInternal
27362742
| _ => schemas->Js.Array2.unsafe_get(idx)
@@ -3098,7 +3104,7 @@ module Union = {
30983104
}
30993105
let mut = base(Union)
31003106
mut.anyOf = Some(anyOf->X.Set.toArray)
3101-
mut.refiner = Some(refiner)
3107+
mut.compiler = Some(compiler)
31023108
mut.has = Some(has)
31033109
mut->castToPublic
31043110
}
@@ -3268,10 +3274,10 @@ module Option = {
32683274
}),
32693275
)
32703276
let to = itemOutputSchema.contents->X.Option.getUnsafe->copyWithoutCache
3271-
switch to.refiner {
3272-
| Some(refiner) => {
3273-
to.serializer = Some(refiner)
3274-
%raw(`delete to.refiner`)
3277+
switch to.compiler {
3278+
| Some(compiler) => {
3279+
to.serializer = Some(compiler)
3280+
%raw(`delete to.compiler`)
32753281
}
32763282
| None => to.serializer = Some((_b, ~input, ~selfSchema as _, ~path as _) => input)
32773283
}
@@ -3320,7 +3326,7 @@ module Array = {
33203326
}
33213327
}
33223328

3323-
let arrayRefiner = Builder.make((b, ~input, ~selfSchema, ~path) => {
3329+
let arrayCompiler = Builder.make((b, ~input, ~selfSchema, ~path) => {
33243330
let item = selfSchema.additionalItems->(Obj.magic: option<additionalItems> => internal)
33253331

33263332
let inputVar = b->B.Val.var(input)
@@ -3361,7 +3367,7 @@ module Array = {
33613367
let mut = base(Array)
33623368
mut.additionalItems = Some(Schema(item->castToInternal->castToPublic))
33633369
mut.items = Some(X.Array.immutableEmpty)
3364-
mut.refiner = Some(arrayRefiner)
3370+
mut.compiler = Some(arrayCompiler)
33653371
mut->castToPublic
33663372
}
33673373
}
@@ -3426,7 +3432,7 @@ let deepStrict = schema => {
34263432
}
34273433

34283434
module Dict = {
3429-
let dictRefiner = Builder.make((b, ~input, ~selfSchema, ~path) => {
3435+
let dictCompiler = Builder.make((b, ~input, ~selfSchema, ~path) => {
34303436
let item = selfSchema.additionalItems->(Obj.magic: option<additionalItems> => internal)
34313437

34323438
let inputVar = b->B.Val.var(input)
@@ -3476,7 +3482,7 @@ module Dict = {
34763482
mut.properties = Some(X.Object.immutableEmpty)
34773483
mut.items = Some(X.Array.immutableEmpty)
34783484
mut.additionalItems = Some(Schema(item->castToPublic))
3479-
mut.refiner = Some(dictRefiner)
3485+
mut.compiler = Some(dictCompiler)
34803486
mut->castToPublic
34813487
}
34823488
}
@@ -3556,7 +3562,7 @@ let enableJson = () => {
35563562
"object": true,
35573563
"array": true,
35583564
},
3559-
refiner: Union.refiner,
3565+
compiler: Union.compiler,
35603566
},
35613567
)
35623568
json.defs = Some(defs)
@@ -3586,7 +3592,7 @@ let enableJsonString = {
35863592
jsonString.tag = String
35873593
jsonString.format = Some(JSON)
35883594
jsonString.name = Some(`${jsonName} string`)
3589-
jsonString.refiner = Some(
3595+
jsonString.compiler = Some(
35903596
Builder.make((b, ~input as inputArg, ~selfSchema, ~path) => {
35913597
let inputTagFlag = inputArg.tag->TagFlag.get
35923598
let input = ref(inputArg)
@@ -4003,7 +4009,7 @@ module Schema = {
40034009
},
40044010
})
40054011

4006-
let rec schemaRefiner = (b, ~input: val, ~selfSchema, ~path) => {
4012+
let rec schemaCompiler = (b, ~input: val, ~selfSchema, ~path) => {
40074013
let additionalItems = selfSchema.additionalItems
40084014
let items = selfSchema.items->X.Option.getUnsafe
40094015
let isArray = TagFlag.isArray(selfSchema.tag)
@@ -4118,6 +4124,7 @@ module Schema = {
41184124

41194125
// This should be done in the parser/serializer
41204126
let _ = %raw(`delete mut.refiner`)
4127+
let _ = %raw(`delete mut.compiler`)
41214128

41224129
mut.serializer = Some(
41234130
Builder.make((b, ~input, ~selfSchema, ~path) => {
@@ -4317,7 +4324,7 @@ module Schema = {
43174324
schema.items = Some(items)
43184325
schema.properties = Some(properties)
43194326
schema.additionalItems = Some(globalConfig.defaultAdditionalItems)
4320-
schema.refiner = Some(schemaRefiner)
4327+
schema.compiler = Some(schemaCompiler)
43214328
schema->castToPublic
43224329
}
43234330

@@ -4645,7 +4652,7 @@ module Schema = {
46454652
let mut = base(Array)
46464653
mut.items = Some(items)
46474654
mut.additionalItems = Some(Strict)
4648-
mut.refiner = Some(schemaRefiner)
4655+
mut.compiler = Some(schemaCompiler)
46494656
mut
46504657
} else {
46514658
let cnstr = (definition->Obj.magic)["constructor"]
@@ -4674,7 +4681,7 @@ module Schema = {
46744681
mut.items = Some(items)
46754682
mut.properties = Some(node->(Obj.magic: dict<unknown> => dict<internal>))
46764683
mut.additionalItems = Some(globalConfig.defaultAdditionalItems)
4677-
mut.refiner = Some(schemaRefiner)
4684+
mut.compiler = Some(schemaCompiler)
46784685
mut
46794686
}
46804687
}
@@ -5497,7 +5504,7 @@ let js_merge = (s1, s2) => {
54975504
mut.items = Some(items)
54985505
mut.properties = Some(properties)
54995506
mut.additionalItems = Some(additionalItems1)
5500-
mut.refiner = Some(Schema.schemaRefiner)
5507+
mut.compiler = Some(Schema.schemaCompiler)
55015508
Some(mut->castToPublic)
55025509
| _ => None
55035510
} {

0 commit comments

Comments
 (0)