Skip to content

Commit

Permalink
toml: fix toml encoding of complex types (#19408)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
qtc-de committed Sep 22, 2023
1 parent edc9ff4 commit 3fb1230
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 23 deletions.
41 changes: 41 additions & 0 deletions vlib/toml/tests/encode_and_decode_test.v
Expand Up @@ -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']
Expand Down
68 changes: 45 additions & 23 deletions vlib/toml/toml.v
Expand Up @@ -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.
Expand Down

0 comments on commit 3fb1230

Please sign in to comment.