From 9183b8eb833ed2750c1d4a00103135a3239363ec Mon Sep 17 00:00:00 2001 From: Viacheslav Poturaev Date: Thu, 21 Aug 2025 12:57:02 +0200 Subject: [PATCH 1/4] Infer var type from string --- value.go | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ value_test.go | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 value.go create mode 100644 value_test.go diff --git a/value.go b/value.go new file mode 100644 index 0000000..9e2b45e --- /dev/null +++ b/value.go @@ -0,0 +1,56 @@ +package vars + +import ( + "encoding/json" + "fmt" + "strconv" +) + +// Infer parses a value from a string into a suitable type. +// If s only contains digits, int64 is returned. +// If s is parsed as float, float64 is returned. +// If s has unquoted values of true or false, bool is returned. +// If s is equal to unquoted null, nil is returned. +// If s starts with double quote ", string decoded from JSON is returned (mind JSON escaping rules). +// If s starts with [ or {, any decoded from JSON is returned. +// Otherwise, s is returned as is. +func Infer(s string) any { + switch s { + case "": + return "" + case "true": + return true + case "false": + return false + case "null": + return nil + } + + if s[0] == '"' { + var v string + if err := json.Unmarshal([]byte(s), &v); err != nil { + return fmt.Errorf("infer string value %s: %w", s, err) + } + + return v + } + + if s[0] == '[' || s[0] == '{' { + var v any + if err := json.Unmarshal([]byte(s), &v); err != nil { + return fmt.Errorf("infer JSON value %s: %w", s, err) + } + + return v + } + + if i, err := strconv.ParseInt(s, 10, 64); err == nil { + return i + } + + if f, err := strconv.ParseFloat(s, 64); err == nil { + return f + } + + return s +} diff --git a/value_test.go b/value_test.go new file mode 100644 index 0000000..071ea2d --- /dev/null +++ b/value_test.go @@ -0,0 +1,35 @@ +package vars_test + +import ( + "testing" + + "github.com/godogx/vars" + "github.com/stretchr/testify/assert" +) + +func TestInfer(t *testing.T) { + for _, tc := range []struct { + s string + v any + }{ + {"", ""}, + {"123", int64(123)}, + {"123.45", 123.45}, + {"true", true}, + {"false", false}, + {"null", nil}, + {`"bla\nbla"`, "bla\nbla"}, + {`"bla\nbl...`, "infer string value \"bla\\nbl...: unexpected end of JSON input"}, + {`{"foo":"bar"}`, map[string]any{"foo": "bar"}}, + {`["abc", 1, false, null]`, []any{"abc", 1.0, false, nil}}, + {`{"foo":"ba....`, "infer JSON value {\"foo\":\"ba....: unexpected end of JSON input"}, + } { + t.Run(tc.s, func(t *testing.T) { + v := vars.Infer(tc.s) + if err, ok := v.(error); ok { + v = err.Error() + } + assert.Equal(t, tc.v, v) + }) + } +} From 4c981a569d5a4c6e5d872ac775dce9bc98b11218 Mon Sep 17 00:00:00 2001 From: Viacheslav Poturaev Date: Thu, 21 Aug 2025 12:59:36 +0200 Subject: [PATCH 2/4] Infer var type from string --- .github/workflows/test-unit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml index 978f679..cf57e95 100644 --- a/.github/workflows/test-unit.yml +++ b/.github/workflows/test-unit.yml @@ -21,7 +21,7 @@ jobs: test: strategy: matrix: - go-version: [ stable, oldstable ] + go-version: [ 1.17.x, stable, oldstable ] runs-on: ubuntu-latest steps: - name: Install Go From 7efe98873dc0133c787026a06cb4b84d2c466e81 Mon Sep 17 00:00:00 2001 From: Viacheslav Poturaev Date: Thu, 21 Aug 2025 13:01:14 +0200 Subject: [PATCH 3/4] Infer var type from string --- value.go | 4 ++-- value_test.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/value.go b/value.go index 9e2b45e..509af79 100644 --- a/value.go +++ b/value.go @@ -14,7 +14,7 @@ import ( // If s starts with double quote ", string decoded from JSON is returned (mind JSON escaping rules). // If s starts with [ or {, any decoded from JSON is returned. // Otherwise, s is returned as is. -func Infer(s string) any { +func Infer(s string) interface{} { switch s { case "": return "" @@ -36,7 +36,7 @@ func Infer(s string) any { } if s[0] == '[' || s[0] == '{' { - var v any + var v interface{} if err := json.Unmarshal([]byte(s), &v); err != nil { return fmt.Errorf("infer JSON value %s: %w", s, err) } diff --git a/value_test.go b/value_test.go index 071ea2d..7c1b2c7 100644 --- a/value_test.go +++ b/value_test.go @@ -10,7 +10,7 @@ import ( func TestInfer(t *testing.T) { for _, tc := range []struct { s string - v any + v interface{} }{ {"", ""}, {"123", int64(123)}, @@ -20,8 +20,8 @@ func TestInfer(t *testing.T) { {"null", nil}, {`"bla\nbla"`, "bla\nbla"}, {`"bla\nbl...`, "infer string value \"bla\\nbl...: unexpected end of JSON input"}, - {`{"foo":"bar"}`, map[string]any{"foo": "bar"}}, - {`["abc", 1, false, null]`, []any{"abc", 1.0, false, nil}}, + {`{"foo":"bar"}`, map[string]interface{}{"foo": "bar"}}, + {`["abc", 1, false, null]`, []interface{}{"abc", 1.0, false, nil}}, {`{"foo":"ba....`, "infer JSON value {\"foo\":\"ba....: unexpected end of JSON input"}, } { t.Run(tc.s, func(t *testing.T) { From 79d8cae63199a6201e268cd051f29910fe52bb54 Mon Sep 17 00:00:00 2001 From: Viacheslav Poturaev Date: Thu, 21 Aug 2025 13:02:43 +0200 Subject: [PATCH 4/4] Infer var type from string --- value_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/value_test.go b/value_test.go index 7c1b2c7..f52b0a7 100644 --- a/value_test.go +++ b/value_test.go @@ -29,6 +29,7 @@ func TestInfer(t *testing.T) { if err, ok := v.(error); ok { v = err.Error() } + assert.Equal(t, tc.v, v) }) }