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

Commit

Permalink
Add support for shallow copying via the copy struct tag.
Browse files Browse the repository at this point in the history
  • Loading branch information
mitchellh committed Feb 2, 2021
1 parent b49c601 commit 5db940b
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 0 deletions.
30 changes: 30 additions & 0 deletions copystructure.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,27 @@ import (
"github.com/mitchellh/reflectwalk"
)

const tagKey = "copy"

// Copy returns a deep copy of v.
//
// Copy is unable to copy unexported fields in a struct (lowercase field names).
// Unexported fields can't be reflected by the Go runtime and therefore
// copystructure can't perform any data copies.
//
// For structs, copy behavior can be controlled with struct tags. For example:
//
// struct {
// Name string
// Data *bytes.Buffer `copy:"shallow"`
// }
//
// The available tag values are:
//
// * "shallow" - The field will be be shallow copied. This means that references
// values such as pointers, maps, slices, etc. will be directly assigned
// versus deep copied.
//
func Copy(v interface{}) (interface{}, error) {
return Config{}.Copy(v)
}
Expand Down Expand Up @@ -399,6 +419,16 @@ func (w *walker) StructField(f reflect.StructField, v reflect.Value) error {
// Push the field onto the stack, we'll handle it when we exit
// the struct field in Exit...
w.valPush(reflect.ValueOf(f))

// If we're shallow copying then push the value as-is onto the stack.
if tv := f.Tag.Get(tagKey); tv == "shallow" {
w.valPush(v)

// set ignore depth one level deeper so we don't ignore the value
// we put on the stack but we do ignore diving into it.
w.ignoreDepth = w.depth + 1
}

return nil
}

Expand Down
24 changes: 24 additions & 0 deletions copystructure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,30 @@ func TestCopy_structNil(t *testing.T) {
}
}

func TestCopy_structShallow(t *testing.T) {
type test struct {
Value string
Value2 *string `copy:"shallow"`
}

value2 := "bar"
value2ptr := &value2
v := test{Value: "foo", Value2: value2ptr}

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

vcopy := result.(test)
if vcopy.Value2 != v.Value2 {
t.Fatal("should shallow copy the pointer")
}
}

func TestCopy_structNested(t *testing.T) {
type TestInner struct{}

Expand Down

0 comments on commit 5db940b

Please sign in to comment.