From abb0cf774ffc0526b2eaa1c4320858cddd6f14f5 Mon Sep 17 00:00:00 2001 From: Hitalo Souza <63821277+enghitalo@users.noreply.github.com> Date: Mon, 1 Jan 2024 07:24:32 -0400 Subject: [PATCH] x.json2: support sumtype encoding in a more robust way (#20093) --- vlib/v/tests/bench/bench_json_vs_json2.v | 1 + vlib/x/json2/encode_struct_test.v | 22 +++- vlib/x/json2/encoder.v | 155 ++++++----------------- 3 files changed, 59 insertions(+), 119 deletions(-) diff --git a/vlib/v/tests/bench/bench_json_vs_json2.v b/vlib/v/tests/bench/bench_json_vs_json2.v index 1866600009..e7de33ed0b 100644 --- a/vlib/v/tests/bench/bench_json_vs_json2.v +++ b/vlib/v/tests/bench/bench_json_vs_json2.v @@ -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() ! { diff --git a/vlib/x/json2/encode_struct_test.v b/vlib/x/json2/encode_struct_test.v index fccc9a4271..0749238d0f 100644 --- a/vlib/x/json2/encode_struct_test.v +++ b/vlib/x/json2/encode_struct_test.v @@ -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() { diff --git a/vlib/x/json2/encoder.v b/vlib/x/json2/encoder.v index e7bdbe19f9..c4061b182e 100644 --- a/vlib/x/json2/encoder.v +++ b/vlib/x/json2/encoder.v @@ -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 ' 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')