1
0
Fork 0
mirror of https://github.com/imjasonh/csvstruct synced 2026-07-07 02:32:20 +00:00

Support pointers in decoding

- support omitempty tag for pointer fields
- s/out/got/g
- s/exp/want/g
This commit is contained in:
Jason Hall 2014-12-22 16:50:22 -05:00
parent c875d4a356
commit df50f1e0f5
3 changed files with 74 additions and 46 deletions

View file

@ -9,6 +9,7 @@ import (
"io"
"reflect"
"strconv"
"strings"
)
var textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
@ -116,11 +117,14 @@ func (d *decoder) decodeStruct(v interface{}, line []string) error {
continue
}
n := f.Name
if f.Tag.Get("csv") != "" {
n = f.Tag.Get("csv")
if n == "-" {
omitempty := false
if tag := f.Tag.Get("csv"); tag != "" {
parts := strings.Split(tag, ",")
if parts[0] == "-" {
continue
}
n = parts[0]
omitempty = len(parts) > 1 && parts[1] == "omitempty"
}
idx, ok := d.hm[n]
if !ok {
@ -143,6 +147,15 @@ func (d *decoder) decodeStruct(v interface{}, line []string) error {
panic("unreachable")
}
}
if vf.Kind() == reflect.Ptr {
if omitempty && strv == "" {
continue
}
if vf.IsNil() {
vf.Set(reflect.New(vf.Type().Elem()))
}
vf = vf.Elem()
}
switch vf.Kind() {
case reflect.String:

View file

@ -18,33 +18,33 @@ func TestDecode(t *testing.T) {
for _, c := range []struct {
data string
out []row
want []row
}{{
data: `Foo,Bar,Baz
a,b,c
d,e,f
`,
out: []row{{"a", "b", "c"}, {"d", "e", "f"}},
want: []row{{"a", "b", "c"}, {"d", "e", "f"}},
}, {
// Rows that only have partial data are only partially filled.
data: `Foo,Bar,Baz
a,"",""
"",b,""
`,
out: []row{{"a", "", ""}, {"", "b", ""}},
want: []row{{"a", "", ""}, {"", "b", ""}},
}, {
// Rows that don't define all the columns are partially filled.
data: `Foo,Bar
a,""
"",b
`,
out: []row{{"a", "", ""}, {"", "b", ""}},
want: []row{{"a", "", ""}, {"", "b", ""}},
}, {
// Entirely disjoint columns produce empty structs.
data: `Qux
d
`,
out: []row{{}},
want: []row{{}},
}} {
d := NewDecoder(strings.NewReader(c.data))
rows := []row{}
@ -58,8 +58,8 @@ d
}
rows = append(rows, r)
}
if !reflect.DeepEqual(rows, c.out) {
t.Errorf("unexpected result, got %v, want %v", rows, c.out)
if !reflect.DeepEqual(rows, c.want) {
t.Errorf("unexpected result, got %v, want %v", rows, c.want)
}
if !isDone(d) {
t.Errorf("decoder unexpectedly not done")
@ -76,9 +76,9 @@ func TestDecode_Unexported(t *testing.T) {
a,b`)).DecodeNext(&r); err != nil {
t.Errorf("unexpected error: %v", err)
}
exp := row{Exported: "a"}
if r != exp {
t.Errorf("unexpected result, got %v, want %v", r, exp)
want := row{Exported: "a"}
if r != want {
t.Errorf("unexpected result, got %v, want %v", r, want)
}
}
@ -93,9 +93,9 @@ func TestDecode_Tags(t *testing.T) {
a,b,c`)).DecodeNext(&r); err != nil {
t.Errorf("unexpected error: %v", err)
}
exp := row{"a", "b", ""}
if r != exp {
t.Errorf("unexpected results, got %v, want %v", r, exp)
want := row{"a", "b", ""}
if r != want {
t.Errorf("unexpected results, got %v, want %v", r, want)
}
}
@ -112,9 +112,9 @@ func TestDecode_NonStrings(t *testing.T) {
123,-123456789,123456789,123.456,true`)).DecodeNext(&r); err != nil {
t.Errorf("unexpected error: %v", err)
}
exp := row{123, -123456789, 123456789, 123.456, true}
if r != exp {
t.Errorf("unexpected results, got %v, want %v", r, exp)
want := row{123, -123456789, 123456789, 123.456, true}
if r != want {
t.Errorf("unexpected results, got %v, want %v", r, want)
}
}
@ -146,18 +146,28 @@ func TestDecode_CompatibleTypes(t *testing.T) {
}
func TestDecode_Pointers(t *testing.T) {
t.Skip("pointers are not yet supported")
type row struct {
S string
SP *string
SP *string `csv:",omitempty"`
}
var r row
if err := NewDecoder(strings.NewReader(`S,SP
a,b`)).DecodeNext(&r); err != nil {
t.Errorf("unexpected error: %v", err)
}
if r.S != "a" || r.SP == nil || *r.SP != "b" {
t.Errorf("unexpected results, got %v", r)
b := "b"
for _, c := range []struct {
s string
want row
}{{
`S,SP
a,b`, row{"a", &b},
}, {
`S,SP
a,`, row{"a", nil},
}} {
var r row
if err := NewDecoder(strings.NewReader(c.s)).DecodeNext(&r); err != nil {
t.Errorf("unexpected error: %v", err)
}
if !reflect.DeepEqual(r, c.want) {
t.Errorf("unexpected results, got %v, want %v", r, c.want)
}
}
}
@ -175,9 +185,9 @@ a,b`))
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)
want := row{"a", "b"}
if r != want {
t.Errorf("unexpected result, got %v, want %v", r, want)
}
if !isDone(d) {
t.Errorf("decoder unexpectedly not done")
@ -186,7 +196,7 @@ a,b`))
func TestDecode_Opts(t *testing.T) {
type row struct{ A, B, C string }
exp := []row{{"a", "b", "c"}, {"d", "", "f"}}
want := []row{{"a", "b", "c"}, {"d", "", "f"}}
for _, c := range []struct {
opts DecodeOpts
@ -231,8 +241,8 @@ d,,f
}
rows = append(rows, r)
}
if !reflect.DeepEqual(rows, exp) {
t.Errorf("unexpected result, got %v, want %v", rows, exp)
if !reflect.DeepEqual(rows, want) {
t.Errorf("unexpected result, got %v, want %v", rows, want)
}
if !isDone(d) {
t.Errorf("decoder unexpectedly not done")
@ -244,7 +254,7 @@ func TestDecode_Map(t *testing.T) {
data := `foo,bar,baz
a,b,c
`
exp := map[string]string{
want := map[string]string{
"foo": "a",
"bar": "b",
"baz": "c",
@ -254,8 +264,8 @@ a,b,c
if err := d.DecodeNext(&got); err != nil {
t.Errorf("%v", err)
}
if !reflect.DeepEqual(got, exp) {
t.Errorf("unexpected result, got %v, want %v", got, exp)
if !reflect.DeepEqual(got, want) {
t.Errorf("unexpected result, got %v, want %v", got, want)
}
if !isDone(d) {
t.Errorf("decoder unexpectedly not done")

View file

@ -3,6 +3,7 @@ package csvstruct
import (
"bytes"
"io"
"net"
"reflect"
"testing"
)
@ -13,9 +14,13 @@ func TestRoundTrip(t *testing.T) {
Renamed string `csv:"renamerized"`
Int64 int64
unexported string
StringPtr *string
IP *net.IP
}
s := "foo"
ip := net.IPv4(128, 0, 0, 1)
in := []row{{"a", "b", 123, "ignored"}, {"c", "d", 456, "ignored"}}
in := []row{{"a", "b", 123, "ignored", &s, &ip}, {"c", "d", 456, "ignored", &s, &ip}}
var buf bytes.Buffer
e := NewEncoder(&buf)
@ -24,13 +29,13 @@ func TestRoundTrip(t *testing.T) {
t.Errorf("unexpected error encoding %v: %v", i, err)
}
}
exp := `String,renamerized,Int64
a,b,123
c,d,456
want := `String,renamerized,Int64,StringPtr,IP
a,b,123,foo,128.0.0.1
c,d,456,foo,128.0.0.1
`
got := buf.String()
if got != exp {
t.Errorf("unexpected result, got %s, want %s", got, exp)
if got != want {
t.Errorf("unexpected result, got %s, want %s", got, want)
}
out := []row{}
@ -48,9 +53,9 @@ c,d,456
t.Errorf("decoder unexpectedly not done")
}
// Unexported fields will not survive the roundtrip
expRows := []row{{"a", "b", 123, ""}, {"c", "d", 456, ""}}
if !reflect.DeepEqual(expRows, out) {
t.Errorf("got unexpected result, got %v, want %v", out, expRows)
wantRows := []row{{"a", "b", 123, "", &s, &ip}, {"c", "d", 456, "", &s, &ip}}
if !reflect.DeepEqual(wantRows, out) {
t.Errorf("got unexpected result, got %v, want %v", out, wantRows)
}
}