From 11d70624affe91cc317e0828383f9d4d0c52726b Mon Sep 17 00:00:00 2001 From: Larpon Date: Wed, 24 Nov 2021 19:39:22 +0100 Subject: [PATCH] toml: streamline value() api (#12568) --- vlib/toml/any.v | 66 +++++++++++++++++++++++------- vlib/toml/tests/value_query_test.v | 45 ++++++++++++++++++++ vlib/toml/toml.v | 40 +++++++++++------- 3 files changed, 121 insertions(+), 30 deletions(-) diff --git a/vlib/toml/any.v b/vlib/toml/any.v index 2be1672161..b111e1cdcb 100644 --- a/vlib/toml/any.v +++ b/vlib/toml/any.v @@ -163,24 +163,21 @@ pub fn (a Any) default_to(value Any) Any { } // value queries a value from the map. -// `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 (m map[string]Any) value(key string) Any { - key_split := parse_dotted_key(key) or { return Any(Null{}) } - return m.value_(key_split) + return Any(m).value(key) } -fn (m map[string]Any) value_(key []string) Any { - value := m[key[0]] or { return Any(Null{}) } - // `match` isn't currently very suitable for these types of sum type constructs... - if value is map[string]Any { - if key.len <= 1 { - return value - } - nm := (value as map[string]Any) - return nm.value_(key[1..]) - } - return value +// value queries a value from the array. +// `key` supports a small query syntax scheme: +// The array can be queried with `[0].b[1].[2]`. +// Maps can be queried in "dotted" form e.g. `a.b.c`. +// quoted keys are supported as `a."b.c"` or `a.'b.c'`. +pub fn (a []Any) value(key string) Any { + return Any(a).value(key) } pub fn (a []Any) as_strings() []string { @@ -190,3 +187,42 @@ pub fn (a []Any) as_strings() []string { } return sa } + +// 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`. +// quoted keys are supported as `a."b.c"` or `a.'b.c'`. +// Arrays can be queried with `a[0].b[1].[2]`. +pub fn (a Any) value(key string) Any { + key_split := parse_dotted_key(key) or { return Any(Null{}) } + return a.value_(a, key_split) +} + +// value_ returns the `Any` value found at `key`. +fn (a Any) value_(value Any, key []string) Any { + assert key.len > 0 + mut any_value := Any(Null{}) + k, index := parse_array_key(key[0]) + if k == '' { + arr := value as []Any + any_value = arr[index] or { return Any(Null{}) } + } + if value is map[string]Any { + any_value = value[k] or { return Any(Null{}) } + if index > -1 { + arr := any_value as []Any + any_value = arr[index] or { return Any(Null{}) } + } + } + if key.len <= 1 { + return any_value + } + match any_value { + map[string]Any, []Any { + return a.value_(any_value, key[1..]) + } + else { + return value + } + } +} diff --git a/vlib/toml/tests/value_query_test.v b/vlib/toml/tests/value_query_test.v index 45ccb2c9b3..ef084325e6 100644 --- a/vlib/toml/tests/value_query_test.v +++ b/vlib/toml/tests/value_query_test.v @@ -15,6 +15,22 @@ colors = [ "yellow", [ "transparent" ] ] + +[[tests]] +id = 1 + +[[tests]] +id = 2 + +[values] +test = 2 + +[[themes]] +name = "Ice" +colors = [ + "blue", + "white" +] ' fn test_value_query_in_array() { @@ -29,4 +45,33 @@ fn test_value_query_in_array() { assert value == 'toml' value = toml_doc.value('errors[11]').default_to('').string() assert value == '' + value = toml_doc.value('themes[2].colors[0]').string() + assert value == 'blue' +} + +fn test_any_value_query() { + toml_doc := toml.parse(toml_text) or { panic(err) } + themes := toml_doc.value('themes') + assert themes.value('[0].colors[0]').string() == 'red' + + themes_arr := toml_doc.value('themes') as []toml.Any + assert themes_arr[0].value('colors[0]').string() == 'red' + + mut any := themes + assert any.value('[1].name').string() == 'Lemon' + any = any.value('[1]') + assert any.value('name').string() == 'Lemon' + + any = toml_doc.value('themes').value('[1].colors').value('[1]') + assert any.string() == 'yellow' + + any = toml_doc.value('themes[1]').value('colors[1]') + assert any.string() == 'yellow' + + any = toml_doc.value('themes[1].colors[0]') + assert any.string() == 'green' + + any = toml_doc.value('values') + any = any.value('test') + assert any.int() == 2 } diff --git a/vlib/toml/toml.v b/vlib/toml/toml.v index 8d0bb80224..3bbce08495 100644 --- a/vlib/toml/toml.v +++ b/vlib/toml/toml.v @@ -150,6 +150,21 @@ pub fn parse_dotted_key(key string) ?[]string { return out } +// parse_array_key converts `key` string to a key and index part. +fn parse_array_key(key string) (string, int) { + mut index := -1 + mut k := key + if k.contains('[') { + index = k.all_after('[').all_before(']').int() + if k.starts_with('[') { + k = '' // k.all_after(']') + } else { + k = k.all_before('[') + } + } + return k, index +} + // to_any converts the `Doc` to toml.Any type. pub fn (d Doc) to_any() Any { return d.ast_to_any(d.ast.table) @@ -159,7 +174,7 @@ pub fn (d Doc) to_any() Any { // `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]`. +// Arrays can be queried with `a[0].b[1].[2]`. pub fn (d Doc) value(key string) Any { key_split := parse_dotted_key(key) or { return Any(Null{}) } return d.value_(d.ast.table, key_split) @@ -169,16 +184,8 @@ pub fn (d Doc) value(key string) Any { 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('[') - } - } + k, index := parse_array_key(key[0]) + if k == '' { a := value as []ast.Value ast_value = a[index] or { return Any(Null{}) } @@ -195,11 +202,14 @@ fn (d Doc) value_(value ast.Value, key []string) Any { 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 ast_value is map[string]ast.Value || ast_value is []ast.Value { - return d.value_(ast_value, key[1..]) + match ast_value { + map[string]ast.Value, []ast.Value { + return d.value_(ast_value, key[1..]) + } + else { + return d.ast_to_any(value) + } } - return d.ast_to_any(value) } // ast_to_any converts `from` ast.Value to toml.Any value.