diff --git a/builtin/builtin.mbti b/builtin/builtin.mbti index 7c10d5690..0bf67a7a0 100644 --- a/builtin/builtin.mbti +++ b/builtin/builtin.mbti @@ -150,6 +150,7 @@ impl BigInt { shl(Self, Int) -> Self shr(Self, Int) -> Self to_hex(Self) -> String + to_json(Self) -> Json to_octets(Self, ~length? : Int) -> Bytes to_string(Self) -> String } @@ -308,6 +309,7 @@ impl SourceLoc { pub type UnsafeMaybeUninit impl Unit { op_equal(Unit, Unit) -> Bool + to_json(Unit) -> Json } impl Bool { default() -> Bool @@ -486,6 +488,7 @@ impl Float { reinterpret_as_int(Float) -> Int sqrt(Float) -> Float to_double(Float) -> Double + to_json(Float) -> Json until(Float, Float, ~step : Float = .., ~inclusive : Bool = ..) -> Iter[Float] upto(Float, Float, ~inclusive : Bool = ..) -> Iter[Float] } @@ -525,11 +528,13 @@ impl String { } impl Option { op_equal[X : Eq](X?, X?) -> Bool + to_json[T : ToJson](T?) -> Json to_string[X : Show](X?) -> String unwrap[X](X?) -> X } impl Result { op_equal[T : Eq, E : Eq](Self[T, E], Self[T, E]) -> Bool + to_json[Ok : ToJson, Err : ToJson](Self[Ok, Err]) -> Json } impl FixedArray { blit_to[A](Self[A], Self[A], ~len : Int, ~src_offset : Int = .., ~dst_offset : Int = ..) -> Unit @@ -541,6 +546,7 @@ impl FixedArray { op_get[T](Self[T], Int) -> T op_set[T](Self[T], Int, T) -> Unit set[T](Self[T], Int, T) -> Unit + to_json[X : ToJson](Self[X]) -> Json to_string[X : Show](Self[X]) -> String unsafe_blit[A](Self[A], Int, Self[A], Int, Int) -> Unit } diff --git a/builtin/json.mbt b/builtin/json.mbt index e5e2cb7ae..cfafc27ce 100644 --- a/builtin/json.mbt +++ b/builtin/json.mbt @@ -47,6 +47,10 @@ pub fn Double::to_json(self : Double) -> Json { Number(self) } +pub fn Float::to_json(self : Float) -> Json { + Number(self.to_double()) +} + fn escape_json_string(str : String) -> String { fn to_hex_digit(i : Int) -> Char { if i < 10 { @@ -89,10 +93,25 @@ pub fn Char::to_json(self : Char) -> Json { String(self.to_string()) } +pub fn BigInt::to_json(self : BigInt) -> Json { + String(self.to_string()) +} + pub fn Array::to_json[X : ToJson](self : Array[X]) -> Json { Array(self.map(ToJson::to_json)) } +pub fn FixedArray::to_json[X : ToJson](self : FixedArray[X]) -> Json { + if self.length() == 0 { + return Array([]) + } + let res = FixedArray::make(self.length(), ToJson::to_json(self[0])) + for i in 1.. Json { let object = {} for k, v in self { @@ -100,3 +119,24 @@ pub fn Map::to_json[K : Show, V : ToJson](self : Map[K, V]) -> Json { } Object(object) } + +pub fn Option::to_json[T : ToJson](self : T?) -> Json { + match self { + None => Null + Some(value) => value.to_json() + } +} + +pub fn Result::to_json[Ok : ToJson, Err : ToJson]( + self : Result[Ok, Err] +) -> Json { + match self { + Ok(ok) => Object({ "Ok": ok.to_json() }) + Err(err) => Object({ "Err": err.to_json() }) + } +} + +// unit +pub fn Unit::to_json(_self : Unit) -> Json { + Null +} diff --git a/json/from_json.mbt b/json/from_json.mbt index 0a62dc496..01c8a90d8 100644 --- a/json/from_json.mbt +++ b/json/from_json.mbt @@ -78,6 +78,13 @@ pub impl FromJson for Char with from_json(json, path) { } } +pub impl FromJson for BigInt with from_json(json, path) { + match json { + String(s) => BigInt::from_string(s) + _ => decode_error!(path, "BigInt::from_json: expected number") + } +} + pub impl[X : FromJson] FromJson for Array[X] with from_json(json, path) { match json { Array(a) => { @@ -93,6 +100,27 @@ pub impl[X : FromJson] FromJson for Array[X] with from_json(json, path) { } } +pub impl[X : FromJson] FromJson for FixedArray[X] with from_json(json, path) { + match json { + Array(a) => { + if a.length() == 0 { + return [] + } + let idx = Index(path, index=0) + let res : FixedArray[X] = FixedArray::make( + a.length(), + FromJson::from_json!(a[0], idx), + ) + for i = 1; i < a.length(); i = i + 1 { + idx.update_index_inplace(i) + res[i] = FromJson::from_json!(a[i], idx) + } + res + } + _ => decode_error!(path, "FixedArray::from_json: expected array") + } +} + pub impl[V : FromJson] FromJson for Map[String, V] with from_json(json, path) { match json { Object(obj) => { @@ -107,3 +135,41 @@ pub impl[V : FromJson] FromJson for Map[String, V] with from_json(json, path) { _ => decode_error!(path, "Map::from_json: expected object") } } + +pub impl[T : FromJson] FromJson for T? with from_json(json, path) { + match json { + Null => None + _ => Some(FromJson::from_json!(json, path)) + } +} + +pub impl[Ok : FromJson, Err : FromJson] FromJson for Result[Ok, Err] with from_json( + json, + path +) { + match json { + Object(obj) => { + if obj.size() != 1 { + decode_error!(path, "Result::from_json: expected object with one field") + } + if obj.contains("Ok") { + Ok(FromJson::from_json!(obj["Ok"].unwrap(), path.add_key("Ok"))) + } else if obj.contains("Err") { + Err(FromJson::from_json!(obj["Err"].unwrap(), path.add_key("Err"))) + } else { + decode_error!( + path, "Result::from_json: expected object with Ok or Err field", + ) + } + } + _ => decode_error!(path, "Result::from_json: expected object") + } +} + +// unit +pub impl FromJson for Unit with from_json(json, path) { + match json { + Null => () + _ => decode_error!(path, "Unit::from_json: expected null") + } +} diff --git a/json/from_json_test.mbt b/json/from_json_test.mbt index 2b0595636..85b5faf86 100644 --- a/json/from_json_test.mbt +++ b/json/from_json_test.mbt @@ -122,3 +122,53 @@ test "tuple failure" { , ) } + +test "fixedarray" { + let u = ([1, 2] : FixedArray[_]) + let v : FixedArray[Int] = @json.from_json!(u.to_json()) + inspect!(v, content="[1, 2]") + let u = (["123", "456"] : FixedArray[_]) + let v : FixedArray[String] = @json.from_json!(u.to_json()) + inspect!( + v, + content= + #|["123", "456"] + , + ) + let u = ([(), ()] : FixedArray[Unit]) + let v : FixedArray[Unit] = @json.from_json!(u.to_json()) + inspect!(v, content="[(), ()]") + let u = ([] : FixedArray[Unit]) + let v : Result[FixedArray[Unit], _] = @json.from_json?(u.to_json()) + inspect!(v, content="Ok([])") +} + +test "Option" { + let u : Json = (None : Int?).to_json() + let v : Int? = @json.from_json!(u) + inspect!(v, content="None") + let u = Some(1).to_json() + let v : Int? = @json.from_json!(u) + inspect!(v, content="Some(1)") +} + +test "Result" { + let u : Json = (Ok(1) : Result[Int, String]).to_json() + let v : Result[Int, String] = @json.from_json!(u) + inspect!(v, content="Ok(1)") + let u = (Err("error") : Result[Int, String]).to_json() + let v : Result[Int, String] = @json.from_json!(u) + inspect!(v, content="Err(\"error\")") +} + +test "Unit" { + let u = ().to_json() + let v : Unit = @json.from_json!(u) + inspect!(v, content="()") +} + +test "bigint" { + let u = BigInt::from_string("10238490123489120893478175871203124819").to_json() + let v : BigInt = @json.from_json!(u) + inspect!(v, content="10238490123489120893478175871203124819") +} diff --git a/json/json.mbti b/json/json.mbti index 5f27393dd..481f98fc2 100644 --- a/json/json.mbti +++ b/json/json.mbti @@ -69,6 +69,8 @@ impl Show for JsonDecodeError impl Show for ParseError +impl FromJson for Unit + impl FromJson for Bool impl FromJson for Char @@ -81,8 +83,16 @@ impl FromJson for Double impl FromJson for String +impl FromJson for Option + +impl FromJson for Result + +impl FromJson for FixedArray + impl FromJson for Array +impl FromJson for BigInt + impl FromJson for Map impl FromJson for Tuple(2) diff --git a/json/moon.pkg.json b/json/moon.pkg.json index 44e5e465f..3b95da877 100644 --- a/json/moon.pkg.json +++ b/json/moon.pkg.json @@ -11,6 +11,7 @@ "test-import": [ "moonbitlang/core/result", "moonbitlang/core/option", + "moonbitlang/core/unit", "moonbitlang/core/array" ] } \ No newline at end of file diff --git a/json/to_json_test.mbt b/json/to_json_test.mbt index 2e5d54254..93622ec1b 100644 --- a/json/to_json_test.mbt +++ b/json/to_json_test.mbt @@ -29,6 +29,10 @@ test "Double::to_json" { inspect!(42.0.to_json(), content="Number(42)") } +test "Float::to_json" { + inspect!((42.0 : Float).to_json(), content="Number(42)") +} + test "String::to_json" { inspect!( "abc".to_json(), @@ -83,6 +87,13 @@ test "Char::to_json" { ) } +test "BigInt::to_json" { + inspect!( + BigInt::from_string("123456789012345678901234567890").to_json(), + content="String(\"123456789012345678901234567890\")", + ) +} + test "Array::to_json" { inspect!( [1, 2, 3].to_json(), @@ -96,6 +107,19 @@ test "Array::to_json" { ) } +test "FixedArray::to_json" { + inspect!( + ([1, 2, 3] : FixedArray[_]).to_json(), + content="Array([Number(1), Number(2), Number(3)])", + ) + inspect!( + [[], ["1"], ["1", "2"]].to_json(), + content= + #|Array([Array([]), Array([String("1")]), Array([String("1"), String("2")])]) + , + ) +} + test "Map::to_json" { inspect!( { "x": [1], "y": [2] }.to_json(), @@ -104,3 +128,8 @@ test "Map::to_json" { , ) } + +test "Option::to_json" { + inspect!((Some(42) : Int?).to_json(), content="Number(42)") + inspect!((None : Int?).to_json(), content="Null") +}