| 1 | import x.json2 as json |
| 2 | import time |
| 3 | |
| 4 | enum JobTitle { |
| 5 | manager |
| 6 | executive |
| 7 | worker |
| 8 | } |
| 9 | |
| 10 | struct Employee { |
| 11 | pub mut: |
| 12 | name string |
| 13 | age int |
| 14 | salary f32 |
| 15 | title JobTitle |
| 16 | sub_employee SubEmployee //! FIXME - decode |
| 17 | } |
| 18 | |
| 19 | pub struct SubEmployee { |
| 20 | pub mut: |
| 21 | name string |
| 22 | age int |
| 23 | salary f32 |
| 24 | title JobTitle |
| 25 | } |
| 26 | |
| 27 | fn test_simple() { |
| 28 | sub_employee := SubEmployee{ |
| 29 | name: 'João' |
| 30 | } |
| 31 | x := Employee{'Peter', 28, 95000.5, .worker, sub_employee} |
| 32 | s := json.encode[Employee](x, enum_as_int: true) |
| 33 | assert s == '{"name":"Peter","age":28,"salary":95000.5,"title":2,"sub_employee":{"name":"João","age":0,"salary":0,"title":0}}' |
| 34 | |
| 35 | y := json.decode[Employee](s) or { |
| 36 | println(err) |
| 37 | assert false |
| 38 | return |
| 39 | } |
| 40 | assert y.name == 'Peter' |
| 41 | assert y.age == 28 |
| 42 | assert y.salary == 95000.5 |
| 43 | assert y.title == .worker |
| 44 | // assert y.sub_employee.name == 'João' |
| 45 | assert y.sub_employee.age == 0 |
| 46 | assert y.sub_employee.salary == 0.0 |
| 47 | // assert y.sub_employee.title == .worker //! FIXME |
| 48 | } |
| 49 | |
| 50 | // const currency_id = 'cconst' |
| 51 | |
| 52 | // struct Price { |
| 53 | // net f64 |
| 54 | // currency_id string @[json: currencyId] = currency_id |
| 55 | // } |
| 56 | |
| 57 | struct User2 { |
| 58 | mut: |
| 59 | age int |
| 60 | nums []int |
| 61 | reg_date time.Time |
| 62 | } |
| 63 | |
| 64 | // User struct needs to be `pub mut` for now in order to access and manipulate values |
| 65 | pub struct User { |
| 66 | pub mut: |
| 67 | age int |
| 68 | nums []int |
| 69 | last_name string @[json: lastName] |
| 70 | is_registered bool @[json: IsRegistered] |
| 71 | typ int @[json: 'type'] |
| 72 | pets string @[json: 'pet_animals'; raw] |
| 73 | } |
| 74 | |
| 75 | fn test_parse_user() { |
| 76 | s := '{"age": 10, "nums": [1,2,3], "type": 1, "lastName": "Johnson", "IsRegistered": true, "pet_animals": {"name": "Bob", "animal": "Dog"}}' |
| 77 | |
| 78 | u := json.decode[User](s)! |
| 79 | |
| 80 | assert u.age == 10 |
| 81 | assert u.last_name == 'Johnson' |
| 82 | assert u.is_registered == true |
| 83 | // assert u.nums.len == 3 |
| 84 | // assert u.nums[0] == 1 |
| 85 | // assert u.nums[1] == 2 |
| 86 | // assert u.nums[2] == 3 |
| 87 | assert u.typ == 1 |
| 88 | assert u.pets == '{"name": "Bob", "animal": "Dog"}' |
| 89 | } |
| 90 | |
| 91 | fn test_encode_decode_time() { |
| 92 | user := User2{ |
| 93 | age: 25 |
| 94 | reg_date: time.new(year: 2020, month: 12, day: 22, hour: 7, minute: 23) |
| 95 | } |
| 96 | s := json.encode(user) |
| 97 | assert s == '{"age":25,"nums":[],"reg_date":"2020-12-22T07:23:00.000Z"}' |
| 98 | |
| 99 | assert s.contains('"reg_date":"2020-12-22T07:23:00.000Z"') |
| 100 | user2 := json.decode[User2](s)! |
| 101 | assert user2.reg_date.str() == '2020-12-22 07:23:00' |
| 102 | } |
| 103 | |
| 104 | fn (mut u User) foo() string { |
| 105 | return json.encode(u) |
| 106 | } |
| 107 | |
| 108 | fn test_encode_user() { |
| 109 | mut usr := User{ |
| 110 | age: 10 |
| 111 | nums: [1, 2, 3] |
| 112 | last_name: 'Johnson' |
| 113 | is_registered: true |
| 114 | typ: 0 |
| 115 | pets: 'foo' |
| 116 | } |
| 117 | expected := '{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"foo"}' |
| 118 | out := json.encode[User](usr) |
| 119 | // println(out) |
| 120 | assert out == expected |
| 121 | // Test json.encode on mutable pointers |
| 122 | assert usr.foo() == expected |
| 123 | } |
| 124 | |
| 125 | struct Color { |
| 126 | pub mut: |
| 127 | space string |
| 128 | point string @[raw] |
| 129 | } |
| 130 | |
| 131 | fn test_raw_json_field() { |
| 132 | color := json.decode[Color]('{"space": "YCbCr", "point": {"Y": 123}}') or { |
| 133 | assert false |
| 134 | Color{} |
| 135 | } |
| 136 | assert color.point == '{"Y": 123}' |
| 137 | assert color.space == 'YCbCr' |
| 138 | } |
| 139 | |
| 140 | fn test_bad_raw_json_field() { |
| 141 | color := json.decode[Color]('{"space": "YCbCr"}') or { return } |
| 142 | assert color.point == '' |
| 143 | assert color.space == 'YCbCr' |
| 144 | } |
| 145 | |
| 146 | fn test_encode_map() { |
| 147 | expected := '{"one":1,"two":2,"three":3,"four":4}' |
| 148 | numbers := { |
| 149 | 'one': json.Any(1) |
| 150 | 'two': json.Any(2) |
| 151 | 'three': json.Any(3) |
| 152 | 'four': json.Any(4) |
| 153 | } |
| 154 | // numbers := { |
| 155 | // 'one': 1 |
| 156 | // 'two': 2 |
| 157 | // 'three': 3 |
| 158 | // 'four': 4 |
| 159 | // } |
| 160 | assert json.encode(numbers) == expected |
| 161 | assert numbers.str() == expected |
| 162 | } |
| 163 | |
| 164 | type ID = string |
| 165 | type GG = int |
| 166 | |
| 167 | struct Message { |
| 168 | id ID |
| 169 | ij GG |
| 170 | } |
| 171 | |
| 172 | fn test_encode_alias_struct() { |
| 173 | expected := '{"id":"118499178790780929","ij":999998888}' |
| 174 | msg := Message{'118499178790780929', 999998888} |
| 175 | out := json.encode[Message](msg) |
| 176 | assert out == expected |
| 177 | } |
| 178 | |
| 179 | struct StByteArray { |
| 180 | ba []u8 |
| 181 | } |
| 182 | |
| 183 | fn test_byte_array() { |
| 184 | assert json.encode(StByteArray{ ba: [u8(1), 2, 3, 4, 5] }) == '{"ba":[1,2,3,4,5]}' |
| 185 | } |
| 186 | |
| 187 | struct Bar { |
| 188 | x string |
| 189 | } |
| 190 | |
| 191 | fn bar[T](payload string) !Bar { // ?T doesn't work currently |
| 192 | result := json.decode[T](payload)! |
| 193 | return result |
| 194 | } |
| 195 | |
| 196 | fn test_generic() { |
| 197 | result := bar[Bar]('{"x":"test"}') or { Bar{} } |
| 198 | assert result.x == 'test' |
| 199 | } |
| 200 | |
| 201 | struct Foo[T] { |
| 202 | pub: |
| 203 | name string |
| 204 | data T |
| 205 | } |
| 206 | |
| 207 | fn test_generic_struct() { |
| 208 | foo_int := Foo[int]{'bar', 12} |
| 209 | foo_enc := json.encode(foo_int) |
| 210 | assert foo_enc == '{"name":"bar","data":12}' |
| 211 | foo_dec := json.decode[Foo[int]](foo_enc)! |
| 212 | assert foo_dec.name == 'bar' |
| 213 | assert foo_dec.data == 12 |
| 214 | } |
| 215 | |
| 216 | type StringAlias = string |
| 217 | |
| 218 | // TODO: encode_pretty array, sum types, struct, alias of struct and others... |
| 219 | struct Foo2 { |
| 220 | ux8 u8 |
| 221 | ux16 u16 |
| 222 | ux32 u32 |
| 223 | ux64 u64 |
| 224 | sx8 i8 |
| 225 | sx16 i16 |
| 226 | sx32 int |
| 227 | sx64 i64 |
| 228 | a bool |
| 229 | b string |
| 230 | c StringAlias |
| 231 | } |
| 232 | |
| 233 | fn test_pretty() { |
| 234 | foo := Foo2{1, 2, 3, 4, -1, -2, -3, -4, true, 'abc', 'aliens'} |
| 235 | assert json.encode(foo, prettify: true, indent_string: ' ') == '{ |
| 236 | "ux8": 1, |
| 237 | "ux16": 2, |
| 238 | "ux32": 3, |
| 239 | "ux64": 4, |
| 240 | "sx8": -1, |
| 241 | "sx16": -2, |
| 242 | "sx32": -3, |
| 243 | "sx64": -4, |
| 244 | "a": true, |
| 245 | "b": "abc", |
| 246 | "c": "aliens" |
| 247 | }' |
| 248 | } |
| 249 | |
| 250 | struct Aa { |
| 251 | sub AliasType |
| 252 | } |
| 253 | |
| 254 | struct Bb { |
| 255 | a int |
| 256 | } |
| 257 | |
| 258 | type AliasType = Bb |
| 259 | |
| 260 | fn test_encode_alias_field() { |
| 261 | s := json.encode(Aa{ |
| 262 | sub: Bb{ |
| 263 | a: 1 |
| 264 | } |
| 265 | }) |
| 266 | assert s == '{"sub":{"a":1}}' |
| 267 | } |
| 268 | |
| 269 | struct APrice {} |
| 270 | |
| 271 | pub struct Association { |
| 272 | association &Association = unsafe { nil } |
| 273 | price APrice |
| 274 | } |
| 275 | |
| 276 | fn test_encoding_struct_with_pointers() { |
| 277 | value := Association{ |
| 278 | association: &Association{ |
| 279 | price: APrice{} |
| 280 | } |
| 281 | price: APrice{} |
| 282 | } |
| 283 | // println(value) |
| 284 | assert json.encode(value) == '{"association":{"price":{}},"price":{}}' |
| 285 | } |
| 286 | |
| 287 | pub struct City { |
| 288 | mut: |
| 289 | name string |
| 290 | } |
| 291 | |
| 292 | pub struct Country { |
| 293 | mut: |
| 294 | cities []City |
| 295 | name string |
| 296 | } |
| 297 | |
| 298 | struct Data { |
| 299 | mut: |
| 300 | countries []Country |
| 301 | users map[string]User |
| 302 | extra map[string]map[string]int |
| 303 | } |
| 304 | |
| 305 | fn test_nested_type() { |
| 306 | data_expected := '{"countries":[{"cities":[{"name":"London"},{"name":"Manchester"}],"name":"UK"},{"cities":[{"name":"Donlon"},{"name":"Termanches"}],"name":"KU"}],"users":{"Foo":{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"little foo"},"Boo":{"age":20,"nums":[5,3,1],"lastName":"Smith","IsRegistered":false,"type":4,"pet_animals":"little boo"}},"extra":{"2":{"n1":2,"n2":4,"n3":8,"n4":16},"3":{"n1":3,"n2":9,"n3":27,"n4":81}}}' |
| 307 | data := Data{ |
| 308 | countries: [ |
| 309 | Country{ |
| 310 | name: 'UK' |
| 311 | cities: [City{'London'}, City{'Manchester'}] |
| 312 | }, |
| 313 | Country{ |
| 314 | name: 'KU' |
| 315 | cities: [City{'Donlon'}, City{'Termanches'}] |
| 316 | }, |
| 317 | ] |
| 318 | users: { |
| 319 | 'Foo': User{ |
| 320 | age: 10 |
| 321 | nums: [1, 2, 3] |
| 322 | last_name: 'Johnson' |
| 323 | is_registered: true |
| 324 | typ: 0 |
| 325 | pets: 'little foo' |
| 326 | } |
| 327 | 'Boo': User{ |
| 328 | age: 20 |
| 329 | nums: [5, 3, 1] |
| 330 | last_name: 'Smith' |
| 331 | is_registered: false |
| 332 | typ: 4 |
| 333 | pets: 'little boo' |
| 334 | } |
| 335 | } |
| 336 | extra: { |
| 337 | '2': { |
| 338 | 'n1': 2 |
| 339 | 'n2': 4 |
| 340 | 'n3': 8 |
| 341 | 'n4': 16 |
| 342 | } |
| 343 | '3': { |
| 344 | 'n1': 3 |
| 345 | 'n2': 9 |
| 346 | 'n3': 27 |
| 347 | 'n4': 81 |
| 348 | } |
| 349 | } |
| 350 | } |
| 351 | out := json.encode(data) |
| 352 | assert out == data_expected |
| 353 | } |
| 354 | |
| 355 | struct Human { |
| 356 | name string |
| 357 | } |
| 358 | |
| 359 | struct Item { |
| 360 | tag string |
| 361 | } |
| 362 | |
| 363 | enum Animal { |
| 364 | dog // Will be encoded as `0` |
| 365 | cat |
| 366 | } |
| 367 | |
| 368 | type Entity = Animal | Human | Item | string | time.Time |
| 369 | |
| 370 | struct SomeGame { |
| 371 | title string |
| 372 | player Entity |
| 373 | other []Entity |
| 374 | } |
| 375 | |
| 376 | fn test_encode_decode_sumtype() { |
| 377 | t := time.now() |
| 378 | game := SomeGame{ |
| 379 | title: 'Super Mega Game' |
| 380 | player: Human{'Monke'} |
| 381 | other: [ |
| 382 | Entity(Item{'Pen'}), |
| 383 | Item{'Cookie'}, |
| 384 | Animal.cat, |
| 385 | 'Stool', |
| 386 | t, |
| 387 | ] |
| 388 | } |
| 389 | |
| 390 | enc := json.encode(game, enum_as_int: true) |
| 391 | |
| 392 | assert enc == '{"title":"Super Mega Game","player":{"name":"Monke","_type":"Human"},"other":[{"tag":"Pen","_type":"Item"},{"tag":"Cookie","_type":"Item"},1,"Stool",{"_type":"${typeof(time.Time{}).name.all_after_last('.')}","value":${t.unix()}}]}' |
| 393 | |
| 394 | dec := json.decode[SomeGame](enc)! |
| 395 | |
| 396 | assert game.title == dec.title |
| 397 | assert game.player == dec.player |
| 398 | assert (game.other[2] as Animal) == .cat |
| 399 | assert dec.other[2] == Entity(Animal.cat) |
| 400 | assert (game.other[4] as time.Time).unix() == (dec.other[4] as time.Time).unix() |
| 401 | } |
| 402 | |
| 403 | struct Foo3 { |
| 404 | name string |
| 405 | age int @[omitempty] |
| 406 | } |
| 407 | |
| 408 | // fn test_omit_empty() { |
| 409 | // foo := Foo3{'Bob', 0} |
| 410 | // assert json.encode(foo) == '{"name":"Bob"}' |
| 411 | // assert json.encode_pretty(foo) == '{ |
| 412 | // "name": "Bob" |
| 413 | // }' |
| 414 | // } |
| 415 | |
| 416 | struct Foo31 { |
| 417 | name string |
| 418 | age ?int |
| 419 | } |
| 420 | |
| 421 | fn test_option_instead_of_omit_empty() { |
| 422 | foo := Foo31{ |
| 423 | name: 'Bob' |
| 424 | } |
| 425 | assert json.encode(foo, prettify: true, indent_string: ' ') == '{ |
| 426 | "name": "Bob" |
| 427 | }' |
| 428 | } |
| 429 | |
| 430 | struct Asdasd { |
| 431 | data GamePacketData |
| 432 | } |
| 433 | |
| 434 | type GamePacketData = GPEquipItem | GPScale |
| 435 | |
| 436 | struct GPScale { |
| 437 | value f32 |
| 438 | } |
| 439 | |
| 440 | struct GPEquipItem { |
| 441 | name string |
| 442 | } |
| 443 | |
| 444 | fn create_game_packet(data &GamePacketData) string { |
| 445 | return json.encode(data) |
| 446 | } |
| 447 | |
| 448 | fn test_encode_sumtype_defined_ahead() { |
| 449 | ret := create_game_packet(&GamePacketData(GPScale{})) |
| 450 | assert ret == '{"value":0,"_type":"GPScale"}' |
| 451 | } |
| 452 | |