Skip to content

Commit a27fd27

Browse files
feat: implement auto cast for set value
1 parent e25fdd2 commit a27fd27

2 files changed

Lines changed: 84 additions & 0 deletions

File tree

data.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package tinybird
33
import (
44
"encoding/json"
55
"errors"
6+
"strconv"
67
"strings"
78
)
89

@@ -148,3 +149,29 @@ func set(row Row, parts []string, value any) error {
148149

149150
return nil
150151
}
152+
153+
// SetWithAutoCast works like Set but applies AutoCast to the value before
154+
// storing it, converting numeric strings to their int64 or float64 equivalents.
155+
func (d *Data) SetWithAutoCast(in string, value any) error {
156+
return d.Set(in, AutoCast(value))
157+
}
158+
159+
// AutoCast attempts to infer the real type of a value. If the value is
160+
// a string it tries to parse it as int64 first, then float64. Non-string
161+
// values and strings that cannot be parsed are returned unchanged.
162+
func AutoCast(val any) any {
163+
s, ok := val.(string)
164+
if !ok {
165+
return val
166+
}
167+
168+
if n, err := strconv.ParseInt(s, 10, 64); err == nil {
169+
return n
170+
}
171+
172+
if f, err := strconv.ParseFloat(s, 64); err == nil {
173+
return f
174+
}
175+
176+
return val
177+
}

data_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,45 @@ func TestGet(t *testing.T) {
9292
assert.Equal(t, tinybird.Row{"a": 4}, d.Get("c"))
9393
}
9494

95+
func TestAutoCast(t *testing.T) {
96+
tests := []struct {
97+
name string
98+
val any
99+
expected any
100+
}{
101+
// Non-string values pass through unchanged
102+
{"nil", nil, nil},
103+
{"int", 42, 42},
104+
{"float64", float64(3.14), float64(3.14)},
105+
{"bool", true, true},
106+
107+
// Strings that are integers
108+
{"string int", "42", int64(42)},
109+
{"string negative int", "-999", int64(-999)},
110+
{"string big int", "1125523841434490335", int64(1125523841434490335)},
111+
{"string zero", "0", int64(0)},
112+
113+
// Strings that are floats
114+
{"string float", "3.14", float64(3.14)},
115+
{"string negative float", "-0.5", float64(-0.5)},
116+
{"string float with exponent", "1.5e2", float64(150)},
117+
118+
// Strings that stay as strings
119+
{"plain string", "hello", "hello"},
120+
{"empty string", "", ""},
121+
{"date string", "2022-03-30", "2022-03-30"},
122+
{"datetime string", "2022-03-30 14:34:57", "2022-03-30 14:34:57"},
123+
{"mixed string", "abc123", "abc123"},
124+
}
125+
126+
for _, tt := range tests {
127+
t.Run(tt.name, func(t *testing.T) {
128+
got := tinybird.AutoCast(tt.val)
129+
assert.Equal(t, tt.expected, got)
130+
})
131+
}
132+
}
133+
95134
func TestSet(t *testing.T) {
96135
d := tinybird.Data{
97136
{"a": nil},
@@ -111,3 +150,21 @@ func TestSet(t *testing.T) {
111150
assert.Nil(t, d.Set("d.b.a", 66))
112151
assert.Equal(t, 66, d.Get("d.b.a"))
113152
}
153+
154+
func TestSetWithAutoCast(t *testing.T) {
155+
d := tinybird.Data{
156+
{"a": nil},
157+
}
158+
159+
assert.Nil(t, d.SetWithAutoCast("a", "42"))
160+
assert.Equal(t, int64(42), d.Get("a"))
161+
162+
assert.Nil(t, d.SetWithAutoCast("a", "3.14"))
163+
assert.Equal(t, float64(3.14), d.Get("a"))
164+
165+
assert.Nil(t, d.SetWithAutoCast("a", "hello"))
166+
assert.Equal(t, "hello", d.Get("a"))
167+
168+
assert.Nil(t, d.SetWithAutoCast("a", 99))
169+
assert.Equal(t, 99, d.Get("a"))
170+
}

0 commit comments

Comments
 (0)