// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
pub(all) suberror JsonDecodeError {
JsonDecodeError(path~ : JsonPath, String)
} derive(Eq, Show, Debug)
///|
/// Trait for types that can be converted from `Json`
pub(open) trait FromJson {
from_json(Json, JsonPath) -> Self raise JsonDecodeError
}
///|
pub fn[T : FromJson] from_json(
json : Json,
path? : JsonPath = Root,
) -> T raise JsonDecodeError {
FromJson::from_json(json, path)
}
///|
fn[T] decode_error(path : JsonPath, msg : String) -> T raise JsonDecodeError {
raise JsonDecodeError(path~, msg)
}
///|
pub impl FromJson for Bool with from_json(json, path) {
match json {
Bool(true) => true
Bool(false) => false
_ => decode_error(path, "Bool::from_json: expected boolean")
}
}
///|
pub impl FromJson for Int with from_json(json, path) {
guard json is Number(n) else {
decode_error(path, "Int::from_json: expected number")
}
n.to_int() catch {
error => decode_error(path, "Int::from_json: parsing failure \{error}")
}
}
///|
pub impl FromJson for Int64 with from_json(json, path) {
guard json is Number(n) else {
decode_error(path, "Int64::from_json: expected number")
}
n.to_int64() catch {
error => decode_error(path, "Int64::from_json: parsing failure \{error}")
}
}
///|
pub impl FromJson for UInt with from_json(json, path) {
guard json is Number(n) else {
decode_error(path, "UInt::from_json: expected number")
}
n.to_uint() catch {
error => decode_error(path, "UInt::from_json: parsing failure \{error}")
}
}
///|
pub impl FromJson for UInt64 with from_json(json, path) {
guard json is Number(n) else {
decode_error(path, "UInt64::from_json: expected number")
}
n.to_uint64() catch {
error => decode_error(path, "UInt64::from_json: parsing failure \{error}")
}
}
///|
pub impl FromJson for Double with from_json(json, path) {
match json {
String("NaN") => @double.not_a_number
String("Infinity") => @double.infinity
String("-Infinity") => @double.neg_infinity
Number(n) =>
n.to_double() catch {
error =>
decode_error(path, "Double::from_json: parsing failure \{error}")
}
_ => decode_error(path, "Double::from_json: expected number")
}
}
///|
pub impl FromJson for Float with from_json(json, path) {
match json {
String("NaN") => @float.not_a_number
String("Infinity") => @float.infinity
String("-Infinity") => @float.neg_infinity
Number(n) =>
n.to_float() catch {
error =>
decode_error(path, "Float::from_json: parsing failure \{error}")
}
_ => decode_error(path, "Float::from_json: expected number")
}
}
///|
pub impl FromJson for String with from_json(json, path) {
guard json is String(a) else {
decode_error(path, "String::from_json: expected string")
}
a
}
///|
pub impl FromJson for StringView with from_json(json, path) {
guard json is String(a) else {
decode_error(path, "View::from_json: expected string")
}
a[:]
}
///|
pub impl FromJson for Char with from_json(json, path) {
guard json is String(a) else {
decode_error(path, "Char::from_json: expected string")
}
let len = a.length()
if len == 1 {
a.unsafe_get(0).unsafe_to_char()
} else if len == 2 {
let c1 = a.unsafe_get(0).to_int()
let c2 = a.unsafe_get(1).to_int()
if c1 is (0xD800..=0xDBFF) && c2 is (0xDC00..=0xDFFF) {
let c3 = (c1 << 10) + c2 - 0x35fdc00
c3.unsafe_to_char()
} else {
decode_error(path, "Char::from_json: invalid surrogate pair")
}
} else {
decode_error(path, "Char::from_json: expected single character")
}
}
///|
pub impl[X : FromJson] FromJson for Array[X] with from_json(json, path) {
guard json is Array(a) else {
decode_error(path, "Array::from_json: expected array")
}
guard JsonPath::Index(path, index=0) is (Index(_) as new_path)
a.mapi((i, x) => {
new_path.index = i
FromJson::from_json(x, new_path)
})
}
///|
pub impl[X : FromJson] FromJson for ArrayView[X] with from_json(json, path) {
guard json is Array(a) else {
decode_error(path, "ArrayView::from_json: expected array")
}
guard JsonPath::Index(path, index=0) is (Index(_) as new_path)
a.mapi((i, x) => {
new_path.index = i
FromJson::from_json(x, new_path)
})
}
///|
pub impl[X : FromJson] FromJson for MutArrayView[X] with from_json(json, path) {
guard json is Array(a) else {
decode_error(path, "MutArrayView::from_json: expected array")
}
guard JsonPath::Index(path, index=0) is (Index(_) as new_path)
a
.mapi((i, x) => {
new_path.index = i
FromJson::from_json(x, new_path)
})
.mut_view()
}
///|
pub impl[X : FromJson] FromJson for FixedArray[X] with from_json(json, path) {
guard json is Array(a) else {
decode_error(path, "FixedArray::from_json: expected array")
}
let len = a.length()
if len == 0 {
return []
}
guard JsonPath::Index(path, index=0) is (Index(_) as new_path)
let res = FixedArray::make(
len,
FromJson::from_json(a.unsafe_get(0), new_path),
)
for i in 1.. ()
[.. "\\x", '0'..='9' | 'a'..='f' as x, '0'..='9' | 'a'..='f' as y, .. rest] => {
let upper = (x.to_int() & 0xF) + (x.to_int() >> 6) * 9
let lower = (y.to_int() & 0xF) + (y.to_int() >> 6) * 9
buffer.write_byte(((upper << 4) | lower).to_byte())
continue rest
}
[' '..='~' as ch, .. rest] => {
guard ch != '\\' && ch != '"' else {
decode_error(path, "Bytes::from_json: invalid escape sequence")
}
buffer.write_byte(ch.to_uint().to_byte())
continue rest
}
_ => decode_error(path, "Bytes::from_json: invalid byte sequence")
}
buffer.to_bytes()
}