From 288257f9a42049563251bae2eb83ef8adf63a706 Mon Sep 17 00:00:00 2001 From: Turiiya <34311583+tobealive@users.noreply.github.com> Date: Thu, 20 Apr 2023 23:42:12 +0200 Subject: [PATCH] add decoding of arrays and nested structs --- vlib/toml/tests/encode_and_decode_test.v | 135 ++++++++++++++++++----- vlib/toml/toml.v | 65 ++++++++--- 2 files changed, 157 insertions(+), 43 deletions(-) diff --git a/vlib/toml/tests/encode_and_decode_test.v b/vlib/toml/tests/encode_and_decode_test.v index f43344b2ae..39fcca3365 100644 --- a/vlib/toml/tests/encode_and_decode_test.v +++ b/vlib/toml/tests/encode_and_decode_test.v @@ -1,18 +1,25 @@ import toml -enum Rank { - low - medium - high +enum JobTitle { + worker + executive + manager } -struct Planet { - name string - population u64 - size f64 - avg_temp int - has_water bool - rank Rank +struct Pet { + name string + nicknames []string + age u64 + income int + height f32 + has_furr bool + title JobTitle + address Address +} + +struct Address { + street string + city string } struct Employee { @@ -21,20 +28,39 @@ mut: age int salary f32 is_human bool - rank Rank + title JobTitle +} + +struct Arrs { + strs []string + bools []bool + ints []int + i64s []i64 + u64s []u64 + f32s []f32 + f64s []f64 + dts []toml.DateTime + dates []toml.Date + times []toml.Time } fn test_encode_and_decode() { - p := Planet{'Mars', 0, 144.8, -81, true, .high} - s := 'name = "Mars" -population = 0 -size = 144.8 -avg_temp = -81 -has_water = true -rank = 2' + p := Pet{'Mr. Scratchy McEvilPaws', ['Freddy', 'Fred', 'Charles'], 8, -1, 0.8, true, .manager, Address{'1428 Elm Street', 'Springwood'}} + s := 'name = "Mr. Scratchy McEvilPaws" +nicknames = [ + "Freddy", + "Fred", + "Charles" +] +age = 8 +income = -1 +height = 0.8 +has_furr = true +title = 2 +address = { street = "1428 Elm Street", city = "Springwood" }' - assert toml.encode[Planet](p) == s - assert toml.decode[Planet](s)! == p + assert toml.encode[Pet](p) == s + assert toml.decode[Pet](s)! == p } pub fn (e Employee) to_toml() string { @@ -43,7 +69,7 @@ pub fn (e Employee) to_toml() string { mp['age'] = toml.Any(e.age) mp['salary'] = toml.Any(f32(e.salary) + 5000.0) mp['is_human'] = toml.Any(e.is_human) - mp['rank'] = toml.Any(int(e.rank) + 1) + mp['title'] = toml.Any(int(e.title) + 1) return mp.to_toml() } @@ -53,18 +79,18 @@ pub fn (mut e Employee) from_toml(any toml.Any) { e.age = mp['age'] or { toml.Any(0) }.int() e.salary = mp['salary'] or { toml.Any(0) }.f32() - 15000.0 e.is_human = mp['is_human'] or { toml.Any(false) }.bool() - e.rank = unsafe { Rank(mp['rank'] or { toml.Any(0) }.int() - 2) } + e.title = unsafe { JobTitle(mp['title'] or { toml.Any(0) }.int() - 2) } } fn test_custom_encode_and_decode() { - x := Employee{'Peter', 28, 95000.5, true, .medium} + x := Employee{'Peter', 28, 95000.5, true, .executive} s := toml.encode[Employee](x) eprintln('Employee x: ${s}') assert s == r'name = "Peter" age = 28 salary = 100000.5 is_human = true -rank = 2' +title = 2' y := toml.decode[Employee](s) or { println(err) @@ -76,5 +102,62 @@ rank = 2' assert y.age == 28 assert y.salary == 85000.5 assert y.is_human == true - assert y.rank == .low + assert y.title == .worker +} + +fn test_array_encode_decode() { + a := Arrs{ + strs: ['foo', 'bar'] + bools: [true, false] + ints: [-1, 2] + i64s: [i64(-2)] + u64s: [u64(123)] + f32s: [f32(1.0), f32(2.5)] + f64s: [100000.5, -123.0] + dts: [toml.DateTime{'1979-05-27T07:32:00Z'}, toml.DateTime{'1979-05-27T07:32:00Z'}] + dates: [toml.Date{'1979-05-27'}, toml.Date{'2022-12-31'}] + times: [toml.Time{'07:32:59'}, toml.Time{'17:32:04'}] + } + + s := 'strs = [ + "foo", + "bar" +] +bools = [ + true, + false +] +ints = [ + -1, + 2 +] +i64s = [ + -2 +] +u64s = [ + 123 +] +f32s = [ + 1.0, + 2.5 +] +f64s = [ + 100000.5, + -123.0 +] +dts = [ + 1979-05-27T07:32:00Z, + 1979-05-27T07:32:00Z +] +dates = [ + 1979-05-27, + 2022-12-31 +] +times = [ + 07:32:59, + 17:32:04 +]' + + assert toml.encode[Arrs](a) == s + assert toml.decode[Arrs](s)! == a } diff --git a/vlib/toml/toml.v b/vlib/toml/toml.v index e3f70a1746..6f454da26b 100644 --- a/vlib/toml/toml.v +++ b/vlib/toml/toml.v @@ -7,7 +7,6 @@ import toml.ast import toml.input import toml.scanner import toml.parser -import time // Null is used in sumtype checks as a "default" value when nothing else is possible. pub struct Null { @@ -24,37 +23,63 @@ pub fn decode[T](toml_txt string) !T { return typ } } - typ = decode_struct[T](doc.to_any()) + decode_struct[T](doc.to_any(), mut typ) return typ } -fn decode_struct[T](doc Any) T { - mut typ := T{} +fn decode_struct[T](doc Any, mut typ T) { $for field in T.fields { + value := doc.value(field.name) $if field.is_enum { - typ.$(field.name) = doc.value(field.name).int() + typ.$(field.name) = value.int() } $else $if field.typ is string { - typ.$(field.name) = doc.value(field.name).string() + typ.$(field.name) = value.string() } $else $if field.typ is bool { - typ.$(field.name) = doc.value(field.name).bool() + typ.$(field.name) = value.bool() } $else $if field.typ is int { - typ.$(field.name) = doc.value(field.name).int() + typ.$(field.name) = value.int() } $else $if field.typ is i64 { - typ.$(field.name) = doc.value(field.name).i64() + typ.$(field.name) = value.i64() } $else $if field.typ is u64 { - typ.$(field.name) = doc.value(field.name).u64() + typ.$(field.name) = value.u64() } $else $if field.typ is f32 { - typ.$(field.name) = doc.value(field.name).f32() + typ.$(field.name) = value.f32() } $else $if field.typ is f64 { - typ.$(field.name) = doc.value(field.name).f64() + typ.$(field.name) = value.f64() + } $else $if field.is_array { + arr := value.array() + match typeof(typ.$(field.name)).name { + '[]string' { typ.$(field.name) = arr.as_strings() } + '[]int' { typ.$(field.name) = arr.map(it.int()) } + '[]i64' { typ.$(field.name) = arr.map(it.i64()) } + '[]u64' { typ.$(field.name) = arr.map(it.u64()) } + '[]f32' { typ.$(field.name) = arr.map(it.f32()) } + '[]f64' { typ.$(field.name) = arr.map(it.f64()) } + '[]bool' { typ.$(field.name) = arr.map(it.bool()) } + '[]toml.DateTime' { typ.$(field.name) = arr.map(it.datetime()) } + '[]toml.Date' { typ.$(field.name) = arr.map(it.date()) } + '[]toml.Time' { typ.$(field.name) = arr.map(it.time()) } + else {} + } } $else $if field.is_struct { - // typ.$(field.name) = decode_struct(doc.value(field.name)) - } $else $if field.typ is time.Time { - // TODO: extend - typ.$(field.name) = doc.value(field.name).datetime() + match typeof(typ.$(field.name)).name { + 'toml.DateTime' { + // typ.$(field.name) = DateTime{value.string()} + } + 'toml.Date' { + // typ.$(field.name) = Date{value.string()} + } + 'toml.Time' { + // typ.$(field.name) = Time{value.string()} + } + else { + mut s := typ.$(field.name) + decode_struct(value, mut s) + typ.$(field.name) = s + } + } } } - return typ } // encode encodes the type `T` into a TOML string. @@ -77,6 +102,12 @@ fn encode_struct[T](typ T) map[string]Any { 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 { + arr << Any(v) + } + mp[field.name] = arr } $else { mp[field.name] = Any(value) }