From 2b4f7e7685e5df2c1d14aa08af55c04647b0e487 Mon Sep 17 00:00:00 2001 From: Larpon Date: Fri, 21 Jan 2022 20:21:31 +0100 Subject: [PATCH] toml: add `encode` and `decode` (#13244) --- vlib/toml/any.v | 75 ++++++++++++++++++++++++ vlib/toml/tests/encode_and_decode_test.v | 58 ++++++++++++++++++ vlib/toml/toml.v | 14 +++++ 3 files changed, 147 insertions(+) create mode 100644 vlib/toml/tests/encode_and_decode_test.v diff --git a/vlib/toml/any.v b/vlib/toml/any.v index 0aeb808484..12df98e8b3 100644 --- a/vlib/toml/any.v +++ b/vlib/toml/any.v @@ -33,6 +33,41 @@ pub fn (a Any) string() string { } } +// to_toml returns `Any` as a TOML encoded value. +pub fn (a Any) to_toml() string { + match a { + map[string]Any { + // TODO more format control? + return a.to_inline_toml() + } + []Any { + return a.to_toml() + } + bool, f32, f64, i64, int, u64 { + return a.str().clone() + } + // NOTE if `.clone()` is not used here: + // string { return a as string } + // ... certain call-patterns to this function will cause a memory corruption. + // See `tests/toml_memory_corruption_test.v` for a matching regression test. + string { + return '"' + (a as string).clone() + '"' + } + DateTime { + return a.str().clone() + } + Date { + return a.str().clone() + } + Time { + return a.str().clone() + } + else { + return a.str().clone() + } + } +} + // int returns `Any` as an 32-bit integer. pub fn (a Any) int() int { match a { @@ -181,6 +216,35 @@ pub fn (m map[string]Any) as_strings() map[string]string { return result } +// to_toml returns the contents of the map +// as a TOML encoded `string`. +pub fn (m map[string]Any) to_toml() string { + mut toml_text := '' + for k, v in m { + mut key := k + if key.contains(' ') { + key = '"$key"' + } + toml_text += '$key = ' + v.to_toml() + '\n' + } + toml_text = toml_text.trim_right('\n') + return toml_text +} + +// to_inline_toml returns the contents of the map +// as an inline table encoded TOML `string`. +pub fn (m map[string]Any) to_inline_toml() string { + mut toml_text := '{' + for k, v in m { + mut key := k + if key.contains(' ') { + key = '"$key"' + } + toml_text += ' $key = ' + v.to_toml() + ',' + } + return toml_text + ' }' +} + // value queries a value from the array. // `key` supports a small query syntax scheme: // The array can be queried with `[0].b[1].[2]`. @@ -200,6 +264,17 @@ pub fn (a []Any) as_strings() []string { return sa } +// to_toml returns the contents of the array +// as a TOML encoded `string`. +pub fn (a []Any) to_toml() string { + mut toml_text := '[\n' + for any in a { + toml_text += ' ' + any.to_toml() + ',\n' + } + toml_text = toml_text.trim_right(',\n') + return toml_text + '\n]' +} + // value queries a value from the `Any` type. // `key` supports a small query syntax scheme: // Maps can be queried in "dotted" form e.g. `a.b.c`. diff --git a/vlib/toml/tests/encode_and_decode_test.v b/vlib/toml/tests/encode_and_decode_test.v new file mode 100644 index 0000000000..a22079ad1c --- /dev/null +++ b/vlib/toml/tests/encode_and_decode_test.v @@ -0,0 +1,58 @@ +import toml + +enum JobTitle { + manager + executive + worker +} + +struct Employee { +pub mut: + name string + age int + salary f32 + is_human bool + title JobTitle +} + +fn (e Employee) to_toml() string { + mut mp := map[string]toml.Any{} + mp['name'] = toml.Any(e.name) + mp['age'] = toml.Any(e.age) + mp['salary'] = toml.Any(e.salary) + mp['is_human'] = toml.Any(e.is_human) + mp['title'] = toml.Any(int(e.title)) + return mp.to_toml() +} + +fn (mut e Employee) from_toml(any toml.Any) { + mp := any.as_map() + e.name = mp['name'] or { toml.Any('') }.string() + e.age = mp['age'] or { toml.Any(0) }.int() + e.salary = mp['salary'] or { toml.Any(0) }.f32() + e.is_human = mp['is_human'] or { toml.Any(false) }.bool() + e.title = JobTitle(mp['title'] or { toml.Any(0) }.int()) +} + +fn test_encode_and_decode() { + x := Employee{'Peter', 28, 95000.5, true, .worker} + s := toml.encode(x) + eprintln('Employee x: $s') + assert s == r'name = "Peter" +age = 28 +salary = 95000.5 +is_human = true +title = 2' + + y := toml.decode(s) or { + println(err) + assert false + return + } + eprintln('Employee y: $y') + assert y.name == 'Peter' + assert y.age == 28 + assert y.salary == 95000.5 + assert y.is_human == true + assert y.title == .worker +} diff --git a/vlib/toml/toml.v b/vlib/toml/toml.v index a38394b9fc..8fc0c619e6 100644 --- a/vlib/toml/toml.v +++ b/vlib/toml/toml.v @@ -12,6 +12,20 @@ import toml.parser pub struct Null { } +// decode decodes a TOML `string` into the target type `T`. +pub fn decode(toml_txt string) ?T { + doc := parse_text(toml_txt) ? + mut typ := T{} + typ.from_toml(doc.to_any()) + return typ +} + +// encode encodes the type `T` into a JSON string. +// Currently encode expects the method `.to_toml()` exists on `T`. +pub fn encode(typ T) string { + return typ.to_toml() +} + // DateTime is the representation of an RFC 3339 datetime string. pub struct DateTime { datetime string