Skip to content
This repository has been archived by the owner on Jul 22, 2024. It is now read-only.

Commit

Permalink
support pointer to collections case
Browse files Browse the repository at this point in the history
  • Loading branch information
Stas Binko authored and sbinq committed Jan 10, 2017
1 parent fa7e5b3 commit c35361e
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 10 deletions.
24 changes: 17 additions & 7 deletions copystructure.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ func (w *walker) Exit(l reflectwalk.Location) error {
case reflectwalk.Map:
fallthrough
case reflectwalk.Slice:
w.replacePointerMaybe()
// Pop map off our container
w.cs = w.cs[:len(w.cs)-1]
case reflectwalk.MapValue:
Expand All @@ -171,16 +172,16 @@ func (w *walker) Exit(l reflectwalk.Location) error {
// or in this case never adds it. We need to create a properly typed
// zero value so that this key can be set.
if !mv.IsValid() {
mv = reflect.Zero(m.Type().Elem())
mv = reflect.Zero(m.Elem().Type().Elem())
}
m.SetMapIndex(mk, mv)
m.Elem().SetMapIndex(mk, mv)
case reflectwalk.SliceElem:
// Pop off the value and the index and set it on the slice
v := w.valPop()
i := w.valPop().Interface().(int)
if v.IsValid() {
s := w.cs[len(w.cs)-1]
se := s.Index(i)
se := s.Elem().Index(i)
if se.CanSet() {
se.Set(v)
}
Expand Down Expand Up @@ -220,9 +221,9 @@ func (w *walker) Map(m reflect.Value) error {
// Create the map. If the map itself is nil, then just make a nil map
var newMap reflect.Value
if m.IsNil() {
newMap = reflect.Indirect(reflect.New(m.Type()))
newMap = reflect.New(m.Type())
} else {
newMap = reflect.MakeMap(m.Type())
newMap = wrapPtr(reflect.MakeMap(m.Type()))
}

w.cs = append(w.cs, newMap)
Expand Down Expand Up @@ -287,9 +288,9 @@ func (w *walker) Slice(s reflect.Value) error {

var newS reflect.Value
if s.IsNil() {
newS = reflect.Indirect(reflect.New(s.Type()))
newS = reflect.New(s.Type())
} else {
newS = reflect.MakeSlice(s.Type(), s.Len(), s.Cap())
newS = wrapPtr(reflect.MakeSlice(s.Type(), s.Len(), s.Cap()))
}

w.cs = append(w.cs, newS)
Expand Down Expand Up @@ -492,3 +493,12 @@ func (w *walker) lock(v reflect.Value) {
locker.Lock()
w.locks[w.depth] = locker
}

func wrapPtr(v reflect.Value) reflect.Value {
if !v.IsValid() {
return v
}
vPtr := reflect.New(v.Type())
vPtr.Elem().Set(v)
return vPtr
}
70 changes: 67 additions & 3 deletions copystructure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,32 @@ func TestCopy_slice(t *testing.T) {
}
}

func TestCopy_pointerToSlice(t *testing.T) {
v := &[]string{"bar", "baz"}

result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}

if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}

func TestCopy_pointerToMap(t *testing.T) {
v := &map[string]string{"bar": "baz"}

result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}

if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}

func TestCopy_struct(t *testing.T) {
type test struct {
Value string
Expand Down Expand Up @@ -188,6 +214,44 @@ func TestCopy_structNested(t *testing.T) {
}
}

func TestCopy_structWithPointerToSliceField(t *testing.T) {
type Test struct {
Value *[]string
}

v := Test{
Value: &[]string{"bar", "baz"},
}

result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}

if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}

func TestCopy_structWithPointerToMapField(t *testing.T) {
type Test struct {
Value *map[string]string
}

v := Test{
Value: &map[string]string{"bar": "baz"},
}

result, err := Copy(v)
if err != nil {
t.Fatalf("err: %s", err)
}

if !reflect.DeepEqual(result, v) {
t.Fatalf("bad: %#v", result)
}
}

func TestCopy_structUnexported(t *testing.T) {
type test struct {
Value string
Expand Down Expand Up @@ -513,14 +577,14 @@ func TestCopy_lockedMap(t *testing.T) {
<-copied

// test that the mutex is in the correct state
result.(lockedMap).Lock()
result.(lockedMap).Unlock()
result.(*lockedMap).Lock()
result.(*lockedMap).Unlock()

if err != nil {
t.Fatalf("err: %s", err)
}

if !reflect.DeepEqual(result, v) {
if !reflect.DeepEqual(result, &v) {
t.Fatalf("bad: %#v", result)
}
}
Expand Down

0 comments on commit c35361e

Please sign in to comment.