Skip to content

Commit

Permalink
implement ToJson/FromJson traits for types defined in core (#985)
Browse files Browse the repository at this point in the history
* feat: float json

* feat: fixedarray json

* feat: bigint json

* feat: option json

* feat: result json

* feat: unit json

* test: fixedarray json

* test: option json

* test: result json

* test: result json

* test: bigint json

---------

Co-authored-by: Hongbo Zhang <[email protected]>
  • Loading branch information
qazxcdswe123 and bobzhang authored Sep 12, 2024
1 parent 63c9927 commit 0ba2a08
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 0 deletions.
6 changes: 6 additions & 0 deletions builtin/builtin.mbti
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -308,6 +309,7 @@ impl SourceLoc {
pub type UnsafeMaybeUninit
impl Unit {
op_equal(Unit, Unit) -> Bool
to_json(Unit) -> Json
}
impl Bool {
default() -> Bool
Expand Down Expand Up @@ -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]
}
Expand Down Expand Up @@ -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
Expand All @@ -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
}
Expand Down
40 changes: 40 additions & 0 deletions builtin/json.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -89,14 +93,50 @@ 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..<self.length() {
res[i] = ToJson::to_json(self[i])
}
Array(Array::from_fixed_array(res))
}

pub fn Map::to_json[K : Show, V : ToJson](self : Map[K, V]) -> Json {
let object = {}
for k, v in self {
object[k.to_string()] = v.to_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
}
66 changes: 66 additions & 0 deletions json/from_json.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -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) => {
Expand All @@ -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")
}
}
50 changes: 50 additions & 0 deletions json/from_json_test.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
10 changes: 10 additions & 0 deletions json/json.mbti
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ impl Show for JsonDecodeError

impl Show for ParseError

impl FromJson for Unit

impl FromJson for Bool

impl FromJson for Char
Expand All @@ -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)
Expand Down
1 change: 1 addition & 0 deletions json/moon.pkg.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"test-import": [
"moonbitlang/core/result",
"moonbitlang/core/option",
"moonbitlang/core/unit",
"moonbitlang/core/array"
]
}
29 changes: 29 additions & 0 deletions json/to_json_test.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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(),
Expand All @@ -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(),
Expand All @@ -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")
}

0 comments on commit 0ba2a08

Please sign in to comment.