From 4b9e8e243cb906c408974a0ef3d7e29828f96958 Mon Sep 17 00:00:00 2001 From: Larpon Date: Sat, 20 Nov 2021 18:45:17 +0100 Subject: [PATCH] toml: support arrays in value key query syntax (#12527) --- vlib/toml/tests/value_query_test.v | 32 +++++++++++++++++++ vlib/toml/toml.v | 50 +++++++++++++++++++++--------- 2 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 vlib/toml/tests/value_query_test.v diff --git a/vlib/toml/tests/value_query_test.v b/vlib/toml/tests/value_query_test.v new file mode 100644 index 0000000000..45ccb2c9b3 --- /dev/null +++ b/vlib/toml/tests/value_query_test.v @@ -0,0 +1,32 @@ +import toml + +const toml_text = ' +modules = [ "ui", "toml" ] +errors = [] + +[[themes]] +name = "Dracula" +colors = [ "red", "black", "white" ] + +[[themes]] +name = "Lemon" +colors = [ + "green", + "yellow", + [ "transparent" ] +] +' + +fn test_value_query_in_array() { + toml_doc := toml.parse(toml_text) or { panic(err) } + mut value := toml_doc.value('themes[0].colors[1]').string() + assert value == 'black' + value = toml_doc.value('themes[1].colors[0]').string() + assert value == 'green' + value = toml_doc.value('themes[1].colors[2].[0]').string() + assert value == 'transparent' + value = toml_doc.value('modules[1]').string() + assert value == 'toml' + value = toml_doc.value('errors[11]').default_to('').string() + assert value == '' +} diff --git a/vlib/toml/toml.v b/vlib/toml/toml.v index d86aa09de8..8d0bb80224 100644 --- a/vlib/toml/toml.v +++ b/vlib/toml/toml.v @@ -156,28 +156,48 @@ pub fn (d Doc) to_any() Any { } // value queries a value from the TOML document. -// `key` should be in "dotted" form (`a.b.c`). -// `key` supports quoted keys like `a."b.c"`. +// `key` supports a small query syntax scheme: +// Maps can be queried in "dotted" form e.g. `a.b.c`. +// quoted keys are supported as `a."b.c"` or `a.'b.c'`. +// Arrays can be queried with `a[0].b[1].[2]`. pub fn (d Doc) value(key string) Any { - values := d.ast.table as map[string]ast.Value key_split := parse_dotted_key(key) or { return Any(Null{}) } - return d.value_(values, key_split) + return d.value_(d.ast.table, key_split) } // value_ returns the value found at `key` in the map `values` as `Any` type. -fn (d Doc) value_(values map[string]ast.Value, key []string) Any { - value := values[key[0]] or { - return Any(Null{}) - // TODO decide this - // panic(@MOD + '.' + @STRUCT + '.' + @FN + ' key "$key[0]" does not exist') +fn (d Doc) value_(value ast.Value, key []string) Any { + assert key.len > 0 + mut ast_value := ast.Value(ast.Null{}) + mut index := -1 + mut k := key[0] + if k.contains('[') { + index = k.all_after('[').all_before(']').int() + if k.starts_with('[') { + k = '' // k.all_after(']') + } else { + k = k.all_before('[') + } + } + if k == '' { + a := value as []ast.Value + ast_value = a[index] or { return Any(Null{}) } + } + + if value is map[string]ast.Value { + ast_value = value[k] or { return Any(Null{}) } + if index > -1 { + a := ast_value as []ast.Value + ast_value = a[index] or { return Any(Null{}) } + } + } + + if key.len <= 1 { + return d.ast_to_any(ast_value) } // `match` isn't currently very suitable for these types of sum type constructs... - if value is map[string]ast.Value { - if key.len <= 1 { - return d.ast_to_any(value) - } - m := (value as map[string]ast.Value) - return d.value_(m, key[1..]) + if ast_value is map[string]ast.Value || ast_value is []ast.Value { + return d.value_(ast_value, key[1..]) } return d.ast_to_any(value) }