From 3fb1230cc30272e2ee61bf60afdbf80101e5029b Mon Sep 17 00:00:00 2001 From: Tobias Neitzel <49147108+qtc-de@users.noreply.github.com> Date: Fri, 22 Sep 2023 12:00:10 +0200 Subject: [PATCH] toml: fix toml encoding of complex types (#19408) * Improve default toml encoder for complex types The default toml encoder failed for more complex structs, as certain edge cases were not covered. This commit attempts to fix this by adding additional handling for these edge cases. * Improve map encoding Since maps currently only support keys that have a string representation, we can utilize this during map encoding. * Add test cases for toml encoding Added test cases for toml encoding of complex nested structs and structs that contain maps. * Add additional test cases --- vlib/toml/tests/encode_and_decode_test.v | 41 ++++++++++++++ vlib/toml/toml.v | 68 ++++++++++++++++-------- 2 files changed, 86 insertions(+), 23 deletions(-) diff --git a/vlib/toml/tests/encode_and_decode_test.v b/vlib/toml/tests/encode_and_decode_test.v index f69fbdb2700fcd..d9253021980346 100644 --- a/vlib/toml/tests/encode_and_decode_test.v +++ b/vlib/toml/tests/encode_and_decode_test.v @@ -149,6 +149,47 @@ fn test_custom_encode_of_complex_struct() { ]' } +struct Example3 { + arr_arr [][]Problem +} + +struct Example4 { + mp map[string]Problem +} + +pub fn (example Example3) to_toml() string { + return '[This is Valid]' +} + +pub fn (example Example4) to_toml() string { + return '[This is Valid]' +} + +fn test_custom_encode_of_nested_complex_struct() { + assert toml.encode(Example3{}) == '[This is Valid]' + assert toml.encode(Example4{}) == '[This is Valid]' +} + +struct Example5 { + mp map[string]Problem +} + +fn test_map_encode_of_complex_struct() { + mut mp := map[string]Problem{} + mp['key_one'] = Problem{} + mp['key_two'] = Problem{} + assert toml.encode(Example5{ mp: mp }) == 'mp = { key_one = "a problem", key_two = "a problem" }' +} + +struct Example6 { + ptr voidptr + r rune +} + +fn test_encode_for_exotic_types() { + assert toml.encode(Example6{ ptr: &voidptr(0), r: `🚀` }) == 'ptr = "0x0"\nr = "🚀"' +} + fn test_array_encode_decode() { a := Arrs{ strs: ['foo', 'bar'] diff --git a/vlib/toml/toml.v b/vlib/toml/toml.v index faeacc8f50be69..80dc901032b10f 100644 --- a/vlib/toml/toml.v +++ b/vlib/toml/toml.v @@ -93,32 +93,54 @@ pub fn encode[T](typ T) string { fn encode_struct[T](typ T) map[string]Any { mut mp := map[string]Any{} $for field in T.fields { - value := typ.$(field.name) - $if field.is_enum { - mp[field.name] = Any(int(value)) - } $else $if field.is_struct { - mp[field.name] = encode_struct(value) - } $else $if field.is_array { - mut arr := []Any{} - for v in value { - $if v is Date { - arr << Any(v) - } $else $if v is Time { - arr << Any(v) - } $else $if v is DateTime { - arr << Any(v) - } $else $if v is $struct { - arr << Any(encode(v)) - } $else { - arr << Any(v) - } + mp[field.name] = to_any(typ.$(field.name)) + } + return mp +} + +fn to_any[T](value T) Any { + $if T is $enum { + return Any(int(value)) + } $else $if T is Date { + return Any(value) + } $else $if T is Time { + return Any(value) + } $else $if T is Null { + return Any(value) + } $else $if T is bool { + return Any(value) + } $else $if T is $float { + return Any(value) + } $else $if T is i64 { + return Any(value) + } $else $if T is int { + return Any(value) + } $else $if T is u64 { + return Any(value) + } $else $if T is DateTime { + return Any(value) + } $else $if T is $struct { + $for method in T.methods { + $if method.name == 'to_toml' { + return Any(value.$method()) } - mp[field.name] = arr - } $else { - mp[field.name] = Any(value) } + return encode_struct(value) + } $else $if T is $array { + mut arr := []Any{cap: value.len} + for v in value { + arr << to_any(v) + } + return arr + } $else $if T is $map { + mut mmap := map[string]Any{} + for key, val in value { + mmap['${key}'] = to_any(val) + } + return mmap + } $else { + return Any('${value}') } - return mp } // DateTime is the representation of an RFC 3339 datetime string.