diff --git a/csvstruct.go b/csvstruct.go index 19fe54d..43a6722 100644 --- a/csvstruct.go +++ b/csvstruct.go @@ -1,4 +1,3 @@ -// TODO: support DecodeNext(nil) to skip a line // TODO: NewEncoder/EncodeNext -- header will be fields in first item... // TODO: Encode/Decode map[string]string @@ -34,7 +33,13 @@ func NewDecoder(r io.Reader) Decoder { // DecodeNext reads the next CSV-encoded value from its input and stores it in the value pointed to by v. func (d *decoder) DecodeNext(v interface{}) error { rv := reflect.ValueOf(v) - if rv.Kind() != reflect.Ptr || rv.IsNil() { + + // v is nil, skip this line and proceed. + if rv.Kind() == reflect.Invalid { + _, err := d.read() + return err + } + if rv.Kind() != reflect.Ptr { return errors.New("must be pointer") } rv = rv.Elem() diff --git a/csvstruct_test.go b/csvstruct_test.go index bbbe62b..bbb5087 100644 --- a/csvstruct_test.go +++ b/csvstruct_test.go @@ -58,6 +58,9 @@ d if !reflect.DeepEqual(rows, c.out) { t.Errorf("unexpected result, got %v, want %v", rows, c.out) } + if !isDone(d) { + t.Errorf("decoder unexpectedly not done") + } } } @@ -128,6 +131,33 @@ a,b`)).DecodeNext(&r); err != nil { } } +func TestDecode_DecodeNil(t *testing.T) { + type row struct { + Foo, Bar string + } + d := NewDecoder(strings.NewReader(`Foo,Bar +ignore,this +a,b`)) + if err := d.DecodeNext(nil); err != nil { + t.Errorf("unexpected error while skipping line: %v", err) + } + var r row + if err := d.DecodeNext(&r); err != nil { + t.Errorf("unexpected error decoding after skip: %v", err) + } + exp := row{"a", "b"} + if r != exp { + t.Errorf("unexpected result, got %v, want %v", r, exp) + } + if !isDone(d) { + t.Errorf("decoder unexpectedly not done") + } +} + +func isDone(d Decoder) bool { + return d.DecodeNext(nil) == io.EOF +} + func ExampleDecoder_DecodeNext() { csv := `Foo,Bar,Baz a,b,c