diff --git a/reader.go b/reader.go index 5e06a10..69f4fd5 100644 --- a/reader.go +++ b/reader.go @@ -197,7 +197,19 @@ func setFieldValue(field reflect.Value, value string) error { if err != nil { return err } + if field.OverflowInt(intValue) { + return fmt.Errorf("value %d out of range for type %s", intValue, field.Kind()) + } field.SetInt(intValue) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + uintValue, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + if field.OverflowUint(uintValue) { + return fmt.Errorf("value %d out of range for type %s", uintValue, field.Kind()) + } + field.SetUint(uintValue) case reflect.Float32, reflect.Float64: floatValue, err := strconv.ParseFloat(value, 64) if err != nil { diff --git a/reader_test.go b/reader_test.go index 00b45a2..3e73a5f 100644 --- a/reader_test.go +++ b/reader_test.go @@ -101,9 +101,9 @@ Bob,bob@example.com,25 func TestComplexTypes(t *testing.T) { // CSV data with various types - csvData := `name,created_at,active,score,count,rate,tags -John,2023-01-02T15:04:05Z,true,98.6,42,3.14,test;debug -Jane,2023-06-15T09:30:00Z,false,75.2,100,2.718,prod;live` + csvData := `name,created_at,active,score,count,rate,tags,rank +John,2023-01-02T15:04:05Z,true,98.6,42,3.14,test;debug,1 +Jane,2023-06-15T09:30:00Z,false,75.2,100,2.718,prod;live,42` reader := strings.NewReader(csvData) @@ -125,6 +125,7 @@ Jane,2023-06-15T09:30:00Z,false,75.2,100,2.718,prod;live` Count: 42, Rate: 3.14, Tags: "test;debug", + Rank: 1, }, { Name: "Jane", @@ -134,6 +135,7 @@ Jane,2023-06-15T09:30:00Z,false,75.2,100,2.718,prod;live` Count: 100, Rate: 2.718, Tags: "prod;live", + Rank: 42, }, } @@ -166,3 +168,53 @@ func TestCustomUnmarshaler(t *testing.T) { t.Errorf("Parsed results do not match expected.\nExpected: %+v\nGot: %+v", expected, results) } } + +func TestInvalidData(t *testing.T) { + t.Parallel() + + panickedValue := func(f func()) (value any) { + defer func() { + value = recover() + }() + + f() + return + } + + test := func(t *testing.T, input string, expectedMessage string) { + t.Helper() + + reader := strings.NewReader(input) + + rb, err := rowboat.NewReader[ComplexRecord](reader) + if err != nil { + t.Fatalf("Failed to create RowBoat: %v", err) + } + + actualValue := panickedValue(func() { + slices.Collect(rb.All()) + }) + + if actualValue == nil { + t.Errorf("Expected a panic for input '%s', but it was parsed without error", input) + return + } + + actualError, ok := actualValue.(error) + if !ok { + t.Errorf("Expected a panic for input '%s', but received a non-error value: %v (%[2]T)", input, actualValue) + return + } + + actualMessage := actualError.Error() + if expectedMessage != actualMessage { + t.Errorf("Expected a panic for input %q with error message %q, but got %q", input, expectedMessage, actualMessage) + } + } + + test(t, "score\nnot-float", `error setting field Score: strconv.ParseFloat: parsing "not-float": invalid syntax`) + test(t, "rank\n-1", `error setting field Rank: strconv.ParseUint: parsing "-1": invalid syntax`) + test(t, "rank\n1000", "error setting field Rank: value 1000 out of range for type uint8") + test(t, "int8\n1000", "error setting field Int8: value 1000 out of range for type int8") + test(t, "int8\n-1000", "error setting field Int8: value -1000 out of range for type int8") +} diff --git a/shared_test.go b/shared_test.go index 4261bbf..41eb056 100644 --- a/shared_test.go +++ b/shared_test.go @@ -21,6 +21,8 @@ type ComplexRecord struct { Count int `csv:"count"` Rate float32 `csv:"rate"` Tags string `csv:"tags"` + Rank uint8 `csv:"rank"` + Int8 int8 `csv:"int8"` } type Point struct { diff --git a/writer_test.go b/writer_test.go index 64e55a0..d293b54 100644 --- a/writer_test.go +++ b/writer_test.go @@ -72,6 +72,7 @@ func TestComplexWriter(t *testing.T) { Count: 42, Rate: 3.14, Tags: "test;debug", + Rank: 1, }, { Name: "Jane", @@ -81,6 +82,7 @@ func TestComplexWriter(t *testing.T) { Count: 100, Rate: 2.718, Tags: "prod;live", + Rank: 42, }, } @@ -127,6 +129,7 @@ func TestWriteAll(t *testing.T) { Count: 42, Rate: 3.14, Tags: "test;debug", + Rank: 1, }, { Name: "Jane", @@ -136,6 +139,7 @@ func TestWriteAll(t *testing.T) { Count: 100, Rate: 2.718, Tags: "prod;live", + Rank: 42, }, }