x.json2: support sumtype encoding in a more robust way (#20093)

This commit is contained in:
Hitalo Souza 2024-01-01 07:24:32 -04:00 committed by GitHub
parent 110e9f1ef5
commit abb0cf774f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 59 additions and 119 deletions

View File

@ -93,6 +93,7 @@ fn benchmark_measure_encode_by_type() ! {
measure_json_encode_old_vs_new(StructType[[]int]{})!
measure_json_encode_old_vs_new(StructType[StructType[int]]{ val: StructType[int]{} })!
measure_json_encode_old_vs_new(StructType[Enum]{})!
measure_json_encode_old_vs_new(StructType[SumTypes]{1})!
}
fn benchmark_measure_encode_by_alias_type() ! {

View File

@ -18,7 +18,7 @@ type TimeAlias = time.Time
type StructAlias = StructType[int]
type EnumAlias = Enumerates
type SumTypes = StructType[string] | bool | int | string | time.Time
type SumTypes = StructType[string] | []SumTypes | []string | bool | int | string | time.Time
enum Enumerates {
a
@ -150,6 +150,11 @@ fn test_array() {
val: false
}]
assert json.encode(StructType[[]StructType[bool]]{ val: array_of_struct }) == '{"val":[{"val":true},{"val":false}]}'
assert json.encode(StructType[[][]string]{ val: [['1'], ['2']] }) == '{"val":[["1"],["2"]]}'
// error: cannot use `[][]string` as `[]string` in argument 1 to `x.json2.Encoder.encode_array`
// assert json.encode(StructType[[][][]string]{ val: [[['1']]] }) == '{"val":[[["1"]]]}'
}
fn test_option_array() {
@ -258,7 +263,7 @@ fn test_sumtypes() {
assert json.encode(StructType[SumTypes]{ val: 0 }) == '{"val":0}'
assert json.encode(StructType[SumTypes]{ val: 1 }) == '{"val":1}'
assert json.encode(StructType[SumTypes]{ val: fixed_time }) == '{"val":2022-03-11T13:54:25.000Z}'
assert json.encode(StructType[SumTypes]{ val: fixed_time }) == '{"val":"2022-03-11T13:54:25.000Z"}'
assert json.encode(StructType[StructType[SumTypes]]{
val: StructType[SumTypes]{
@ -266,15 +271,22 @@ fn test_sumtypes() {
}
}) == '{"val":{"val":1}}'
// assert json.encode(StructType[SumTypes]{ val: StructType[string]{
// val: '111111'
// } }) == '{"val":1}'
assert json.encode(StructType[SumTypes]{
val: StructType[string]{
val: '111111'
}
}) == '{"val":{"val":"111111"}}'
assert json.encode(StructType[StructType[SumTypes]]{
val: StructType[SumTypes]{
val: 1
}
}) == '{"val":{"val":1}}'
// assert json.encode(StructType{ val: [SumTypes('a')] }) == '{"val":["a"]}'
// assert json.encode(StructType[SumTypes]{ val: ['a'] }) == '{"val":["a"]}'
// assert json.encode(StructType[SumTypes]{ val: [SumTypes('a')] }) == '{"val":["a"]}'
// assert json.encode(StructType[SumTypes]{ val: '' }) == '{"val":""}'
}
fn test_maps() {

View File

@ -119,10 +119,7 @@ fn (e &Encoder) encode_any(val Any, level int, mut buf []u8) ! {
buf << `"`
}
Null {
buf << `n`
buf << `u`
buf << `l`
buf << `l`
unsafe { buf.push_many(json2.null_in_bytes.str, json2.null_in_bytes.len) }
}
}
}
@ -132,7 +129,8 @@ fn (e &Encoder) encode_map[T](value T, level int, mut buf []u8) ! {
mut idx := 0
for k, v in value {
e.encode_newline(level, mut buf)!
e.encode_string(k.str(), mut buf)!
// e.encode_string(k.str(), mut buf)!
e.encode_string(k, mut buf)!
buf << json2.colon_rune
if e.newline != 0 {
buf << ` `
@ -143,7 +141,8 @@ fn (e &Encoder) encode_map[T](value T, level int, mut buf []u8) ! {
}
idx++
}
e.encode_newline(level, mut buf)!
// e.encode_newline(level, mut buf)!
e.encode_newline(level - 1, mut buf)!
buf << json2.curly_close_rune
}
@ -152,12 +151,23 @@ fn (e &Encoder) encode_value_with_level[T](val T, level int, mut buf []u8) ! {
e.encode_string(val, mut buf)!
} $else $if T is Any {
e.encode_any(val, level, mut buf)!
} $else $if T is map[string]Any {
// weird quirk but val is destructured immediately to Any
e.encode_any(val, level, mut buf)!
} $else $if T is $sumtype {
if val.str() != 'unknown sum type value' {
$for v in val.variants {
if val is v {
e.encode_value_with_level(val, level, mut buf)!
}
}
}
} $else $if T is $alias {
// TODO
} $else $if T is time.Time {
parsed_time := time.parse(val.str()) or { time.Time{} }
e.encode_string(parsed_time.format_rfc3339(), mut buf)!
} $else $if T is $map {
e.encode_map(val, level, mut buf)!
} $else $if T is []Any {
// TODO test
e.encode_any(val, level, mut buf)!
} $else $if T is Encodable {
str_value := val.json_str()
@ -165,17 +175,14 @@ fn (e &Encoder) encode_value_with_level[T](val T, level int, mut buf []u8) ! {
} $else $if T is $struct {
e.encode_struct(val, level, mut buf)!
} $else $if T is $enum {
e.encode_any(Any(int(val)), level, mut buf)!
} $else $if T is bool {
e.encode_any(val, level, mut buf)!
} $else $if T is $float {
e.encode_any(val, level, mut buf)!
} $else $if T is $int {
e.encode_any(val, level, mut buf)!
str_int := int(val).str()
unsafe { buf.push_many(str_int.str, str_int.len) }
} $else $if T is $int || T is $float || T is bool {
str_int := val.str()
unsafe { buf.push_many(str_int.str, str_int.len) }
} $else $if T is Null {
e.encode_any(val, level, mut buf)!
unsafe { buf.push_many(json2.null_in_bytes.str, json2.null_in_bytes.len) }
} $else {
// dump(val.str())
return error('cannot encode value with ${typeof(val).name} type')
}
}
@ -333,72 +340,18 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut buf []u8) ! {
e.encode_map(value, level + 1, mut buf)!
} $else $if field.is_enum {
// TODO - replace for `field.typ is $enum`
str_value := int(val.$(field.name)).str()
unsafe { buf.push_many(str_value.str, str_value.len) }
// str_value := int(val.$(field.name)).str()
// unsafe { buf.push_many(str_value.str, str_value.len) }
e.encode_value_with_level(val.$(field.name), level + 1, mut buf)!
} $else $if field.typ is $enum {
// wr.write(int(val.$(field.name)).str().bytes())! // FIXME - error: cannot cast string to `int`, use `val.$field.name.int()` instead.
} $else $if field.typ is $sumtype {
// dump(val.$(field.name).str())
// dump(is_none)
sum_type_value := value.str()#[typeof(val.$(field.name)).name.len + 1..-1]
is_string := sum_type_value[0] == "'"[0]
// mut is_struct := false
// mut is_sumtype := false
// mut is_enum := false
// mut is_array := false
match sum_type_value[0] {
`0`...`9` {
if sum_type_value.contains_any(' /:-') {
date_time_str := time.parse(sum_type_value)!
unsafe {
str_value := date_time_str.format_rfc3339()
buf.push_many(str_value.str, str_value.len)
}
} else {
unsafe { buf.push_many(sum_type_value.str, sum_type_value.len) }
field_value := val.$(field.name)
if field_value.str() != 'unknown sum type value' {
$for v in field_value.variants {
if field_value is v {
e.encode_value_with_level(field_value, level, mut buf)!
}
}
`A`...`Z` {
// SumTypes(0)
if sum_type_value.contains('(') {
if !sum_type_value.all_before('(').contains_any(' "\'[') {
// is_sumtype = true
}
}
// StructType{
// StructType[int]{
if sum_type_value.contains('{') {
if !sum_type_value.all_before('{').contains_any(' "\'') {
// is_struct = true
// TODO
// e.encode_struct_from_sumtype(value, level + 1, mut buf)!
}
}
}
`a`...`z` {
if sum_type_value in ['true', 'false'] {
unsafe { buf.push_many(sum_type_value.str, sum_type_value.len) }
} else {
// is_enum = true
}
}
else {
// dump('else')
}
}
// dump(sum_type_value)
// dump(is_none)
// dump(is_string)
// dump(is_struct)
// dump(is_sumtype)
// dump(is_enum)
// dump(is_array)
if is_string {
e.encode_string(sum_type_value#[1..-1], mut buf)!
}
} $else $if field.typ is $alias {
$if field.unaliased_typ is string {
@ -416,18 +369,13 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut buf []u8) ! {
str_value := val.$(field.name).str()
unsafe { buf.push_many(str_value.str, str_value.len) }
} $else $if field.unaliased_typ is $array {
// e.encode_array(val.$(field.name), level + 1, mut buf)! // FIXME - error: could not infer generic type `U` in call to `encode_array`
// TODO
} $else $if field.unaliased_typ is $struct {
// e.encode_struct(val.$(field.name), level + 1, mut buf)! // FIXME - error: cannot use `BoolAlias` as `StringAlias` in argument 1 to `x.json2.Encoder.encode_struct`
e.encode_struct(value, level + 1, mut buf)!
} $else $if field.unaliased_typ is $enum {
// enum_value := val.$(field.name)
// dump(int(val.$(field.name))) // FIXME
// dump(val.$(field.name).int()) // FIXME - error: unknown method or field: `BoolAlias.int`
// dump(val.$(field.name).int()) // FIXME - error: cannot convert 'enum <anonymous>' to 'struct string'
// wr.write(val.$(field.name).int().str().bytes())! // FIXME - error: unknown method or field: `BoolAlias.int`
// TODO
} $else $if field.unaliased_typ is $sumtype {
// TODO
} $else {
return error('the alias ${typeof(val).name} cannot be encoded')
}
@ -455,42 +403,21 @@ fn (e &Encoder) encode_array[U](val []U, level int, mut buf []u8) ! {
for i in 0 .. val.len {
e.encode_newline(level, mut buf)!
$if U is string {
e.encode_any(val[i], level + 1, mut buf)!
} $else $if U is bool {
e.encode_any(bool(val[i]), level + 1, mut buf)!
} $else $if U is f32 {
e.encode_any(f32(val[i]), level + 1, mut buf)!
} $else $if U is f64 {
e.encode_any(f64(val[i]), level + 1, mut buf)!
} $else $if U is i8 {
e.encode_any(i8(val[i]), level + 1, mut buf)!
} $else $if U is i16 {
e.encode_any(i16(val[i]), level + 1, mut buf)!
} $else $if U is int {
e.encode_any(int(val[i]), level + 1, mut buf)!
} $else $if U is i64 {
e.encode_any(i64(val[i]), level + 1, mut buf)!
} $else $if U is u8 {
e.encode_any(u8(val[i]), level + 1, mut buf)!
} $else $if U is u16 {
e.encode_any(u16(val[i]), level + 1, mut buf)!
} $else $if U is u32 {
e.encode_any(u32(val[i]), level + 1, mut buf)!
} $else $if U is u64 {
e.encode_any(u64(val[i]), level + 1, mut buf)!
$if U is string || U is bool || U is $int || U is $float {
e.encode_value_with_level(val[i], level + 1, mut buf)!
} $else $if U is $array {
// FIXME - error: could not infer generic type `U` in call to `encode_array`
// e.encode_array(val[i], level + 1, mut buf)!
e.encode_array(val[i], level + 1, mut buf)!
} $else $if U is $struct {
e.encode_struct(val[i], level + 1, mut buf)!
} $else $if U is $sumtype {
// TODO test
$if U is Any {
e.encode_any(val[i], level + 1, mut buf)!
} $else {
// TODO
}
} $else $if U is $enum {
// TODO test
e.encode_any(i64(val[i]), level + 1, mut buf)!
} $else {
return error('type ${typeof(val).name} cannot be array encoded')