package bind_test import ( "code.squareroundforest.org/arpio/bind" "fmt" "testing" "time" ) func TestScan(t *testing.T) { t.Run("conversion", func(t *testing.T) { var i64 int64 i := 42 bind.BindScalar(&i64, i) if i64 != 42 { t.Fatal() } }) t.Run("unscannable", func(t *testing.T) { var s string if bind.BindScalar(&s, func() string { return "" }) { t.Fatal() } }) t.Run("bool", func(t *testing.T) { var b bool if !bind.BindScalar(&b, "true") || !b { t.Fatal() } }) t.Run("int", func(t *testing.T) { var i int if !bind.BindScalar(&i, "42") || i != 42 { t.Fatal() } }) t.Run("int sized", func(t *testing.T) { var i int8 if !bind.BindScalar(&i, "42") || i != 42 { t.Fatal() } }) t.Run("uint", func(t *testing.T) { var i uint if !bind.BindScalar(&i, "42") || i != 42 { t.Fatal() } }) t.Run("uint sized", func(t *testing.T) { var i uint8 if !bind.BindScalar(&i, "42") || i != 42 { t.Fatal() } }) t.Run("binary", func(t *testing.T) { var i int if !bind.BindScalar(&i, "0b101010") || i != 42 { t.Fatal() } }) t.Run("octal", func(t *testing.T) { var i int if !bind.BindScalar(&i, "052") || i != 42 { t.Fatal() } }) t.Run("hexa", func(t *testing.T) { var i int if !bind.BindScalar(&i, "0x2a") || i != 42 { t.Fatal() } }) t.Run("num parse failing", func(t *testing.T) { i := 21 if bind.BindScalar(&i, "foo") || i != 21 { t.Fatal() } }) t.Run("float", func(t *testing.T) { var f float64 if !bind.BindScalar(&f, "2.41") || f != 2.41 { t.Fatal() } }) t.Run("string", func(t *testing.T) { var s string if !bind.BindScalar(&s, "foo bar baz") || s != "foo bar baz" { t.Fatal() } }) t.Run("interface", func(t *testing.T) { var i any if !bind.BindScalar(&i, "foo bar baz") || i != "foo bar baz" { t.Fatal() } }) t.Run("duration", func(t *testing.T) { var d time.Duration if !bind.BindScalar(&d, "9s") || d != 9*time.Second { t.Fatal(d) } }) t.Run("duration convert", func(t *testing.T) { // not supported, otherwise any integer field would become convertible: type dur time.Duration var d dur if bind.BindScalar(&d, "9s") { t.Fatal(d) } }) t.Run("time rfc", func(t *testing.T) { var tim time.Time if !bind.BindScalar(&tim, "2025-08-31T01:14:55+02:00") || tim.Year() != 2025 { t.Fatal(tim) } }) t.Run("time only", func(t *testing.T) { var tim time.Time if !bind.BindScalar(&tim, "01:14:55") || tim.Second() != 55 { t.Fatal(tim) } }) t.Run("time convert", func(t *testing.T) { type timey time.Time var tim timey if !bind.BindScalar(&tim, "2025-08-31T01:14:55+02:00") || time.Time(tim).Year() != 2025 { t.Fatal(tim) } }) t.Run("time from int", func(t *testing.T) { const timestamp = 1757097011 var tim time.Time if !bind.BindScalar(&tim, timestamp) || !tim.Equal(time.Unix(timestamp, 0)) { t.Fatal() } }) t.Run("time from float", func(t *testing.T) { const ( seconds = 1757097011 nanoseconds = 33000 timestamp = float64(1757097011.000033) ) var tim time.Time if !bind.BindScalar(&tim, timestamp) || tim.Unix() != seconds { t.Fatal(tim, tim.Unix(), tim.UnixNano()) } }) t.Run("time from int string", func(t *testing.T) { const seconds = 1757097011 var tim time.Time if !bind.BindScalar(&tim, fmt.Sprint(seconds)) || tim.Unix() != seconds { t.Fatal() } }) t.Run("time from float string", func(t *testing.T) { const ( seconds = 1757097011 nanoseconds = 33000 s = "1757097011.000033" ) var tim time.Time if !bind.BindScalar(&tim, s) || tim.Unix() != seconds || tim.Nanosecond() != nanoseconds { t.Fatal(tim.Unix(), tim.Nanosecond()) } }) t.Run("time from invalid float string", func(t *testing.T) { t.Run("multiple decimal points", func(t *testing.T) { var tim time.Time if bind.BindScalar(&tim, "24.235.23") { t.Fatal() } }) t.Run("invalid round part", func(t *testing.T) { var tim time.Time if bind.BindScalar(&tim, "89s.01") { t.Fatal() } }) t.Run("invalid fractional part", func(t *testing.T) { var tim time.Time if bind.BindScalar(&tim, "89.01s") { t.Fatal() } }) }) t.Run("time invalid", func(t *testing.T) { var tim time.Time if bind.BindScalar(&tim, "foo") { t.Fatal() } }) }