Skip to content
Open
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
274 changes: 146 additions & 128 deletions src/Json_Decode.res
Original file line number Diff line number Diff line change
Expand Up @@ -55,149 +55,134 @@ let string = (. json) => {
Obj.magic(json)
}

let array = (decode) => (. json) => {
if !Js.Array.isArray(json) {
Error.expected("array", json)
}
let array = decode => {
(. json) => {
if !Js.Array.isArray(json) {
Error.expected("array", json)
}

let source: array<Js.Json.t> = Obj.magic(json)
let target = %raw("new Array(json.length)")
let source: array<Js.Json.t> = Obj.magic(json)
let target = %raw("new Array(json.length)")

for i in 0 to Array.length(source) - 1 {
try {
let value = decode(. %raw("json[i]"))
target->Array.unsafe_set(i, value)
} catch {
| DecodeError(msg) => raise(DecodeError(`${msg}\n\tin array at index $i`))
for i in 0 to Array.length(source) - 1 {
try {
let value = decode(. %raw("json[i]"))
target->Array.unsafe_set(i, value)
} catch {
| DecodeError(msg) => raise(DecodeError(`${msg}\n\tin array at index $i`))
}
}
}

target
target
}
}

let list = (decode) => (. json) => array(decode)(. json)->Array.to_list
let list = decode => {(. json) => array(decode)(. json)->Array.to_list}

let option = decode => (. json) => {
if Obj.magic(json) == Js.null {
None
} else {
Some(decode(. json))
let option = decode => {
(. json) => {
if Obj.magic(json) == Js.null {
None
} else {
Some(decode(. json))
}
}
}

let date = (. json) => string(. json)->Js.Date.fromString

let tuple2 = (decodeA, decodeB) => (. json) => {
if !Js.Array.isArray(json) {
Error.expected("array", json)
}
let tuple2 = (decodeA, decodeB) => {
(. json) => {
if !Js.Array.isArray(json) {
Error.expected("array", json)
}

let arr: array<Js.Json.t> = Obj.magic(json)
if Array.length(arr) != 2 {
raise(
DecodeError(
`Expected array of length 2, got array of length ${Array.length(arr)->string_of_int}`,
),
)
}
let arr: array<Js.Json.t> = Obj.magic(json)
if Array.length(arr) != 2 {
raise(
DecodeError(
`Expected array of length 2, got array of length ${Array.length(arr)->string_of_int}`,
),
)
}

try (decodeA(. arr->Array.unsafe_get(0)), decodeB(. arr->Array.unsafe_get(1))) catch {
| DecodeError(msg) => raise(DecodeError(`${msg}\n\tin pair`))
try (decodeA(. arr->Array.unsafe_get(0)), decodeB(. arr->Array.unsafe_get(1))) catch {
| DecodeError(msg) => raise(DecodeError(`${msg}\n\tin pair`))
}
}
}
let pair = tuple2

let tuple3 = (decodeA, decodeB, decodeC) => (. json) => {
if !Js.Array.isArray(json) {
Error.expected("array", json)
}
let tuple3 = (decodeA, decodeB, decodeC) => {
(. json) => {
if !Js.Array.isArray(json) {
Error.expected("array", json)
}

let arr: array<Js.Json.t> = Obj.magic(json)
if Array.length(arr) != 3 {
raise(
DecodeError(
`Expected array of length 3, got array of length ${Array.length(arr)->string_of_int}`,
),
)
}
let arr: array<Js.Json.t> = Obj.magic(json)
if Array.length(arr) != 3 {
raise(
DecodeError(
`Expected array of length 3, got array of length ${Array.length(arr)->string_of_int}`,
),
)
}

try (
decodeA(. arr->Array.unsafe_get(0)),
decodeB(. arr->Array.unsafe_get(1)),
decodeC(. arr->Array.unsafe_get(2)),
) catch {
| DecodeError(msg) => raise(DecodeError(`${msg}\n\tin pair`))
try (
decodeA(. arr->Array.unsafe_get(0)),
decodeB(. arr->Array.unsafe_get(1)),
decodeC(. arr->Array.unsafe_get(2)),
) catch {
| DecodeError(msg) => raise(DecodeError(`${msg}\n\tin pair`))
}
}
}

let tuple4 = (decodeA, decodeB, decodeC, decodeD) => (. json) => {
if !Js.Array.isArray(json) {
Error.expected("array", json)
}

let arr: array<Js.Json.t> = Obj.magic(json)
if Array.length(arr) != 4 {
raise(
DecodeError(
`Expected array of length 4, got array of length ${Array.length(arr)->string_of_int}`,
),
)
}

try (
decodeA(. arr->Array.unsafe_get(0)),
decodeB(. arr->Array.unsafe_get(1)),
decodeC(. arr->Array.unsafe_get(2)),
decodeD(. arr->Array.unsafe_get(3)),
) catch {
| DecodeError(msg) => raise(DecodeError(`${msg}\n\tin pair`))
}
}
let tuple4 = (decodeA, decodeB, decodeC, decodeD) => {
(. json) => {
if !Js.Array.isArray(json) {
Error.expected("array", json)
}

let dict = decode => (. json) => {
if Js.typeof(json) != "object" || Js.Array.isArray(json) || Obj.magic(json) == Js.null {
Error.expected("object", json)
}
let arr: array<Js.Json.t> = Obj.magic(json)
if Array.length(arr) != 4 {
raise(
DecodeError(
`Expected array of length 4, got array of length ${Array.length(arr)->string_of_int}`,
),
)
}

let source: Js.Dict.t<Js.Json.t> = Obj.magic(json)
try Js.Dict.map(decode, source)->Obj.magic catch {
| DecodeError(msg) => raise(DecodeError(`${msg}\n\tin dict'`))
try (
decodeA(. arr->Array.unsafe_get(0)),
decodeB(. arr->Array.unsafe_get(1)),
decodeC(. arr->Array.unsafe_get(2)),
decodeD(. arr->Array.unsafe_get(3)),
) catch {
| DecodeError(msg) => raise(DecodeError(`${msg}\n\tin pair`))
}
}
}

let field = (key, decode) => (. json) => {
if Js.typeof(json) != "object" || Js.Array.isArray(json) || Obj.magic(json) == Js.null {
Error.expected("object", json)
}

if !(%raw("key in json")) {
raise(DecodeError(`${key} required`))
}
let dict = decode => {
(. json) => {
if Js.typeof(json) != "object" || Js.Array.isArray(json) || Obj.magic(json) == Js.null {
Error.expected("object", json)
}

try decode(. %raw("json[key]")) catch {
| DecodeError(msg) => raise(DecodeError(`${msg}\n\tat field '${key}'`))
let source: Js.Dict.t<Js.Json.t> = Obj.magic(json)
try Js.Dict.map(decode, source)->Obj.magic catch {
| DecodeError(msg) => raise(DecodeError(`${msg}\n\tin dict'`))
}
}
}

let object = f => (. json) => {
if Js.typeof(json) != "object" || Js.Array.isArray(json) || Obj.magic(json) == Js.null {
raise(Error.expected("object", json))
}

let optional = (. key, decode) => {
if !(%raw("key in json")) {
None
} else {
try {
let value = decode(. %raw("json[key]"))
Some(value)
} catch {
| DecodeError(msg) => raise(DecodeError(`${msg}\n\tat field '${key}'`))
}
let field = (key, decode) => {
(. json) => {
if Js.typeof(json) != "object" || Js.Array.isArray(json) || Obj.magic(json) == Js.null {
Error.expected("object", json)
}
}

let required = (. key, decode) => {
if !(%raw("key in json")) {
raise(DecodeError(`${key} required`))
}
Expand All @@ -206,33 +191,66 @@ let object = f => (. json) => {
| DecodeError(msg) => raise(DecodeError(`${msg}\n\tat field '${key}'`))
}
}

f({optional: optional, required: required})
}

let oneOf = decoders => (. json) => {
let errors = []
let object = f => {
(. json) => {
if Js.typeof(json) != "object" || Js.Array.isArray(json) || Obj.magic(json) == Js.null {
raise(Error.expected("object", json))
}

let rec loop = i => {
if i >= Array.length(decoders) {
raise(
DecodeError(
`All decoders given to oneOf failed. Here are all the errors:\n- ${errors->Js.Array2.joinWith(
"\n",
)}\nAnd the JSON being decoded: ${stringify(json)}`,
),
)
let optional = (. key, decode) => {
if !(%raw("key in json")) {
None
} else {
try {
let value = decode(. %raw("json[key]"))
Some(value)
} catch {
| DecodeError(msg) => raise(DecodeError(`${msg}\n\tat field '${key}'`))
}
}
}

let decode = Array.unsafe_get(decoders, i)
try decode(. json) catch {
| DecodeError(err) =>
errors->Js.Array2.push(err)->ignore
loop(i + 1)
let required = (. key, decode) => {
if !(%raw("key in json")) {
raise(DecodeError(`${key} required`))
}

try decode(. %raw("json[key]")) catch {
| DecodeError(msg) => raise(DecodeError(`${msg}\n\tat field '${key}'`))
}
}

f({optional, required})
}
}

let oneOf = decoders => {
(. json) => {
let errors = []

let rec loop = i => {
if i >= Array.length(decoders) {
raise(
DecodeError(
`All decoders given to oneOf failed. Here are all the errors:\n- ${errors->Js.Array2.joinWith(
"\n",
)}\nAnd the JSON being decoded: ${stringify(json)}`,
),
)
}

loop(0)
let decode = Array.unsafe_get(decoders, i)
try decode(. json) catch {
| DecodeError(err) =>
errors->Js.Array2.push(err)->ignore
loop(i + 1)
}
}

loop(0)
}
}

let map = (decode, f) => (. json) => f(. decode(. json))
Expand Down
46 changes: 25 additions & 21 deletions src/Json_Encode.res
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,34 @@ external boolArray: array<bool> => Js.Json.t = "%identity"

@val external null: Js.Json.t = "null"

let array = encode => arr => arr->Js.Array2.map(x => encode(x))->jsonArray

let list = encode => l =>
switch l {
| list{} => jsonArray([])
| list{hd, ...tl} =>
let arr = Array.make(l->List.length, hd->encode)
let rec fill = (i, l) =>
switch l {
| list{} => arr
| list{hd, ...tl} =>
Array.unsafe_set(arr, i, hd->encode)
fill(i + 1, tl)
}
fill(1, tl)->jsonArray
}
let array = encode => {arr => arr->Js.Array2.map(x => encode(x))->jsonArray}

let list = encode => {
l =>
switch l {
| list{} => jsonArray([])
| list{hd, ...tl} =>
let arr = Array.make(l->List.length, hd->encode)
let rec fill = (i, l) =>
switch l {
| list{} => arr
| list{hd, ...tl} =>
Array.unsafe_set(arr, i, hd->encode)
fill(i + 1, tl)
}
fill(1, tl)->jsonArray
}
}

let object = props => props->Js.Dict.fromArray->jsonDict

let option = encode => opt =>
switch opt {
| None => null
| Some(v) => v->encode
}
let option = encode => {
opt =>
switch opt {
| None => null
| Some(v) => v->encode
}
}

let withDefault = (default, encode, opt) =>
switch opt {
Expand Down