diff --git a/csvstruct.go b/csvstruct.go index 556740a..1eb9799 100644 --- a/csvstruct.go +++ b/csvstruct.go @@ -1,71 +1,52 @@ -package main +package csvstruct import ( "encoding/csv" - "fmt" "io" "reflect" - "strings" ) -type example struct { - Foo string `csv:"foo"` - Bar string +type Decoder interface { + DecodeNext(v interface{}) error } -func main() { - data := ` -foo,Bar,baz -a,b,c -d,e,f -g,h,i` - ch := make(chan interface{}) - r, _ := newCsvReader(strings.NewReader(data)) - e := example{} - _ = r.Read(e, ch) +type decoder struct { + hm map[string]int + r csv.Reader } -type csvReader struct { - header []string - r csv.Reader -} - -func newCsvReader(r io.Reader) (*csvReader, error) { - csvr := csv.NewReader(r) - header, err := csvr.Read() - if err != nil { - return nil, err +func NewDecoder(r io.Reader) Decoder { + return decoder{ + r: *csv.NewReader(r), } - c := csvReader{ - header: header, - r: *csvr, - } - return &c, nil } -func (r csvReader) Read(in interface{}, ch chan<- interface{}) error { - t := reflect.TypeOf(in) - hm := reverse(r.header) - - line, err := r.r.Read() - for err != io.EOF { +func (d decoder) DecodeNext(in interface{}) error { + if len(d.hm) == 0 { + // First run; read header row + header, err := d.r.Read() if err != nil { return err } - out := reflect.ValueOf(&in) - for i := 0; i < t.NumField(); i++ { - f := t.Field(i) - n := f.Name - if f.Tag.Get("csv") == "" { - n = f.Tag.Get("csv") - } - val := line[hm[n]] - v := reflect.ValueOf(&val) - v.Elem().SetString(line[hm[n]]) - out.Elem().Set(v) - } - fmt.Println("%v", out.Elem().Interface()) - line, err = r.r.Read() + d.hm = reverse(header) + } + + t := reflect.TypeOf(in) + + line, err := d.r.Read() + if err != nil { + return err + } + + out := reflect.ValueOf(in) + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + n := f.Name + // TODO: f.Tag.Get("csv") + val := line[d.hm[n]] + v := reflect.ValueOf(&val) + v.Elem().SetString(line[d.hm[n]]) + out.Elem().Set(v) } return nil } @@ -76,4 +57,4 @@ func reverse(in []string) map[string]int { m[v] = i } return m -} \ No newline at end of file +} diff --git a/csvstruct_test.go b/csvstruct_test.go new file mode 100644 index 0000000..81a398f --- /dev/null +++ b/csvstruct_test.go @@ -0,0 +1,53 @@ +package csvstruct + +import ( + "io" + "strings" + "testing" +) + +func TestDecode(t *testing.T) { + type row struct { + Foo, Bar, Baz string + } + + for _, c := range []struct { + data string + out []row + }{{ + data: `Foo,Bar,Baz +a,b,c +d,e,f`, + out: []row{{"a", "b", "c"}, {"d", "e", "f"}}, + }, { + // Rows that only have partial data are only partially filled. + data: `Foo,Bar +a, +,b`, + out: []row{{"a", "", ""}, {"", "b", ""}}, + }, { + // Disjoint data results in unset struct. + data: ` +Qux +ignored`, + out: []row{{"", "", ""}}, + }} { + d := NewDecoder(strings.NewReader(c.data)) + var row row + i := 0 + for { + err := d.DecodeNext(&row) + if err == io.EOF { + break + } + if err != nil { + t.Errorf("%v", err) + break + } + if c.out[i] != row { + t.Errorf("unexpected item %d, got %+v, want %+v", i, row, c.out[i]) + } + i++ + } + } +}