Skip to content

Commit c43483b

Browse files
committed
Support S.string->S.to(S.json)->S.to(S.bigint)
1 parent 95078a0 commit c43483b

File tree

4 files changed

+373
-301
lines changed

4 files changed

+373
-301
lines changed

packages/sury/src/Sury.res

Lines changed: 92 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,7 @@ var d = Object.defineProperty, p = SuryError.prototype;
631631
d(p, 'message', {
632632
get() {
633633
return message(this);
634-
}
634+
},
635635
})
636636
d(p, 'reason', {
637637
get() {
@@ -1587,6 +1587,12 @@ let failTransform = (b, ~inputVar, ~path, ~target) => {
15871587
)
15881588
}
15891589

1590+
let jsonName = `JSON`
1591+
1592+
let inputToString = (b, input: val) => {
1593+
b->B.val(`""+${input.inline}`, ~schema=string)
1594+
}
1595+
15901596
let rec parse = (prevB: b, ~schema, ~input as inputArg: val, ~path) => {
15911597
let b = B.scope(prevB)
15921598

@@ -1606,8 +1612,21 @@ let rec parse = (prevB: b, ~schema, ~input as inputArg: val, ~path) => {
16061612
let isSameTag = input.contents.tag === schema.tag
16071613
let schemaTagFlag = TagFlag.get(schema.tag)
16081614
let inputTagFlag = TagFlag.get(input.contents.tag)
1615+
let isUnsupported = ref(false)
16091616
if schemaTagFlag->Flag.unsafeHas(TagFlag.union->Flag.with(TagFlag.unknown)) {
16101617
()
1618+
} else if !(inputTagFlag->Flag.unsafeHas(TagFlag.unknown)) && schema.name === Some(jsonName) {
1619+
if (
1620+
inputTagFlag->Flag.unsafeHas(
1621+
TagFlag.string->Flag.with(TagFlag.number)->Flag.with(TagFlag.boolean),
1622+
)
1623+
) {
1624+
()
1625+
} else if inputTagFlag->Flag.unsafeHas(TagFlag.bigint) {
1626+
input := b->inputToString(input.contents)
1627+
} else {
1628+
isUnsupported := true
1629+
}
16111630
} else if isSchemaLiteral {
16121631
if isFromLiteral {
16131632
if input.contents.const !== schema.const {
@@ -1666,34 +1685,82 @@ let rec parse = (prevB: b, ~schema, ~input as inputArg: val, ~path) => {
16661685
inline: `"${const}"`,
16671686
}
16681687
} else {
1669-
unsupportedTransform(~from=input.contents->Obj.magic, ~target=schema)
1688+
isUnsupported := true
16701689
}
16711690
} else if inputTagFlag->Flag.unsafeHas(TagFlag.unknown) {
1672-
if b.global.flag->Flag.unsafeHas(Flag.typeValidation) {
1673-
b.filterCode = prevB->B.typeFilterCode(~schema, ~input=input.contents, ~path)
1674-
}
1675-
// FIXME: Make it simpler
1676-
let refined = b->B.makeRefinedOf(~input=input.contents, ~schema)
1677-
input.contents.tag = refined.tag
1678-
input.contents.inline = refined.inline
1679-
input.contents.var = refined.var
1680-
input.contents.additionalItems = refined.additionalItems
1681-
input.contents.properties = refined.properties
1682-
if refined->Obj.magic->isLiteral {
1683-
input.contents.const = refined.const
1691+
switch schema.ref {
1692+
| Some(ref) =>
1693+
let defs = b.global.defs->X.Option.getUnsafe
1694+
// Ignore #/$defs/
1695+
let identifier = ref->Js.String2.sliceToEnd(~from=8)
1696+
let def = defs->Js.Dict.unsafeGet(identifier)
1697+
let flag = if schema.noValidation->X.Option.getUnsafe {
1698+
b.global.flag->Flag.without(Flag.typeValidation)
1699+
} else {
1700+
b.global.flag
1701+
}
1702+
let recOperation = switch def->Obj.magic->X.Dict.unsafeGetOptionByInt(flag) {
1703+
| Some(fn) =>
1704+
// A hacky way to prevent infinite recursion
1705+
if fn === %raw(`0`) {
1706+
b->B.embed(def) ++ `[${flag->X.Int.unsafeToString}]`
1707+
} else {
1708+
b->B.embed(fn)
1709+
}
1710+
| None => {
1711+
def
1712+
->Obj.magic
1713+
->X.Dict.setByInt(flag, 0)
1714+
let fn = internalCompile(~schema=def, ~flag, ~defs=b.global.defs)
1715+
def
1716+
->Obj.magic
1717+
->X.Dict.setByInt(flag, fn)
1718+
b->B.embed(fn)
1719+
}
1720+
}
1721+
input :=
1722+
b->B.withPathPrepend(~input=input.contents, ~path, (_, ~input, ~path as _) => {
1723+
let output = B.Val.map(recOperation, input)
1724+
if def.isAsync === None {
1725+
let defsMut = defs->X.Dict.copy
1726+
defsMut->Js.Dict.set(identifier, unknown)
1727+
let _ = def->isAsyncInternal(~defs=Some(defsMut))
1728+
}
1729+
if def.isAsync->X.Option.getUnsafe {
1730+
output.flag = output.flag->Flag.with(ValFlag.async)
1731+
}
1732+
output
1733+
})
1734+
// Force rec function execution
1735+
// for the case when the value is not used
1736+
let _ = input.contents.var(b)
1737+
| None => {
1738+
if b.global.flag->Flag.unsafeHas(Flag.typeValidation) {
1739+
b.filterCode = prevB->B.typeFilterCode(~schema, ~input=input.contents, ~path)
1740+
}
1741+
// FIXME: Make it simpler
1742+
let refined = b->B.makeRefinedOf(~input=input.contents, ~schema)
1743+
input.contents.tag = refined.tag
1744+
input.contents.inline = refined.inline
1745+
input.contents.var = refined.var
1746+
input.contents.additionalItems = refined.additionalItems
1747+
input.contents.properties = refined.properties
1748+
if refined->Obj.magic->isLiteral {
1749+
input.contents.const = refined.const
1750+
}
1751+
}
16841752
}
16851753
} else if (
16861754
schemaTagFlag->Flag.unsafeHas(TagFlag.string) &&
16871755
inputTagFlag->Flag.unsafeHas(
16881756
TagFlag.boolean->Flag.with(TagFlag.number->Flag.with(TagFlag.bigint)),
16891757
)
16901758
) {
1691-
input := b->B.val(`""+${input.contents.inline}`, ~schema)
1759+
input := b->inputToString(input.contents)
16921760
} else if !isSameTag {
16931761
if inputTagFlag->Flag.unsafeHas(TagFlag.string) {
16941762
let inputVar = input.contents.var(b)
1695-
switch schema {
1696-
| {tag: Boolean} =>
1763+
if schemaTagFlag->Flag.unsafeHas(TagFlag.boolean) {
16971764
let output = b->B.allocateVal(~schema) // FIXME: schema should be only simple bool
16981765
b.code =
16991766
b.code ++
@@ -1703,13 +1770,12 @@ let rec parse = (prevB: b, ~schema, ~input as inputArg: val, ~path) => {
17031770
~target=schema,
17041771
)};`
17051772
input := output
1706-
1707-
| {tag: Number, ?format} =>
1773+
} else if schemaTagFlag->Flag.unsafeHas(TagFlag.number) {
17081774
let output = b->B.val(`+${inputVar}`, ~schema) // FIXME: schema
17091775
let outputVar = output.var(b)
17101776
b.code =
17111777
b.code ++
1712-
switch format {
1778+
switch schema.format {
17131779
| None => `Number.isNaN(${outputVar})`
17141780
| Some(_) =>
17151781
`(${b
@@ -1718,8 +1784,7 @@ let rec parse = (prevB: b, ~schema, ~input as inputArg: val, ~path) => {
17181784
} ++
17191785
`&&${b->failTransform(~inputVar, ~path, ~target=schema)};`
17201786
input := output
1721-
1722-
| {tag: BigInt} =>
1787+
} else if schemaTagFlag->Flag.unsafeHas(TagFlag.bigint) {
17231788
let output = b->B.allocateVal(~schema) // FIXME:
17241789
b.code =
17251790
b.code ++
@@ -1729,60 +1794,16 @@ let rec parse = (prevB: b, ~schema, ~input as inputArg: val, ~path) => {
17291794
~target=schema,
17301795
)}}`
17311796
input := output
1732-
| _ => unsupportedTransform(~from=input.contents->Obj.magic, ~target=schema)
1797+
} else {
1798+
isUnsupported := true
17331799
}
17341800
} else {
1735-
unsupportedTransform(~from=input.contents->Obj.magic, ~target=schema)
1801+
isUnsupported := true
17361802
}
17371803
}
17381804

1739-
switch schema.ref {
1740-
| Some(ref) =>
1741-
let defs = b.global.defs->X.Option.getUnsafe
1742-
// Ignore #/$defs/
1743-
let identifier = ref->Js.String2.sliceToEnd(~from=8)
1744-
let def = defs->Js.Dict.unsafeGet(identifier)
1745-
let flag = if schema.noValidation->X.Option.getUnsafe {
1746-
b.global.flag->Flag.without(Flag.typeValidation)
1747-
} else {
1748-
b.global.flag
1749-
}
1750-
let recOperation = switch def->Obj.magic->X.Dict.unsafeGetOptionByInt(flag) {
1751-
| Some(fn) =>
1752-
// A hacky way to prevent infinite recursion
1753-
if fn === %raw(`0`) {
1754-
b->B.embed(def) ++ `[${flag->X.Int.unsafeToString}]`
1755-
} else {
1756-
b->B.embed(fn)
1757-
}
1758-
| None => {
1759-
def
1760-
->Obj.magic
1761-
->X.Dict.setByInt(flag, 0)
1762-
let fn = internalCompile(~schema=def, ~flag, ~defs=b.global.defs)
1763-
def
1764-
->Obj.magic
1765-
->X.Dict.setByInt(flag, fn)
1766-
b->B.embed(fn)
1767-
}
1768-
}
1769-
input :=
1770-
b->B.withPathPrepend(~input=input.contents, ~path, (_, ~input, ~path as _) => {
1771-
let output = B.Val.map(recOperation, input)
1772-
if def.isAsync === None {
1773-
let defsMut = defs->X.Dict.copy
1774-
defsMut->Js.Dict.set(identifier, unknown)
1775-
let _ = def->isAsyncInternal(~defs=Some(defsMut))
1776-
}
1777-
if def.isAsync->X.Option.getUnsafe {
1778-
output.flag = output.flag->Flag.with(ValFlag.async)
1779-
}
1780-
output
1781-
})
1782-
// Force rec function execution
1783-
// for the case when the value is not used
1784-
let _ = input.contents.var(b)
1785-
| None => ()
1805+
if isUnsupported.contents {
1806+
unsupportedTransform(~from=input.contents->Obj.magic, ~target=schema)
17861807
}
17871808

17881809
switch schema.refiner {
@@ -3327,7 +3348,6 @@ module String = {
33273348
let datetimeRe = %re(`/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/`)
33283349
}
33293350

3330-
let jsonName = `JSON`
33313351
let json = {
33323352
let jsonRef = base(Ref)
33333353
jsonRef.ref = Some(`${defsPath}${jsonName}`)
@@ -3380,8 +3400,6 @@ module JsonString = {
33803400
message => OperationFailed(message),
33813401
"t.message",
33823402
)}}`
3383-
3384-
// b->B.typeValidation(~schema=item, ~input=jsonVal, ~path)
33853403
jsonVal
33863404
}),
33873405
)

0 commit comments

Comments
 (0)