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:
parent
c875d4a356
commit
df50f1e0f5
3 changed files with 74 additions and 46 deletions
19
decode.go
19
decode.go
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue