From 697f29ebfa29834ce7380767bc175ee3bb4987ca Mon Sep 17 00:00:00 2001 From: Artemij Shepelev Date: Fri, 21 Sep 2018 17:07:39 +0300 Subject: [PATCH 1/3] implemented struct tags "-" and "=" --- deepcopy.go | 12 +++++++++++- deepcopy_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/deepcopy.go b/deepcopy.go index ba763ad..3b95b6a 100644 --- a/deepcopy.go +++ b/deepcopy.go @@ -87,12 +87,22 @@ func copyRecursive(original, cpy reflect.Value) { } // Go through each field of the struct and copy it. for i := 0; i < original.NumField(); i++ { + field := original.Type().Field(i) // The Type's StructField for a given field is checked to see if StructField.PkgPath // is set to determine if the field is exported or not because CanSet() returns false // for settable fields. I'm not sure why. -mohae - if original.Type().Field(i).PkgPath != "" { + if field.PkgPath != "" { continue } + + switch field.Tag.Get("deepcopy") { + case "-": + continue + case "=": + cpy.Field(i).Set(original.Field(i)) + continue + } + copyRecursive(original.Field(i), cpy.Field(i)) } diff --git a/deepcopy_test.go b/deepcopy_test.go index f150b1a..cc7a0e0 100644 --- a/deepcopy_test.go +++ b/deepcopy_test.go @@ -908,6 +908,45 @@ func TestPointerToStruct(t *testing.T) { } } +func TestFieldSkipByTag(t *testing.T) { + type Foo struct { + Bar int `deepcopy:"-"` + Thing int + } + + f := Foo{Bar: 42, Thing: 44} + cpy := Copy(f).(Foo) + + if !reflect.DeepEqual(cpy, Foo{Thing: 44}) { // Bar is skipped + t.Errorf("expected the copy to be equal to the original (except for memory location); it wasn't: got %#v; want %#v", f, cpy) + } +} + +func TestFieldAssignByTag(t *testing.T) { + type Inner struct { + Field int + } + type Foo struct { + Bar *Inner `deepcopy:"="` + Thing int + } + + inner := &Inner{Field: 100} + f := Foo{Bar: inner, Thing: 44} + cpy := Copy(f).(Foo) + + if !reflect.DeepEqual(cpy, Foo{Bar: inner, Thing: 44}) { // Bar is skipped + t.Errorf("expected the copy to be equal to the original (except for memory location); it wasn't: got %#v; want %#v", f, cpy) + } + + inner.Field = 101 + if inner.Field != f.Bar.Field || f.Bar.Field != cpy.Bar.Field { + t.Errorf("expected that pointer-assigned value will change in both original and copy: %d != %d != %d", + inner.Field, f.Bar.Field, cpy.Bar.Field) + } + +} + func TestIssue9(t *testing.T) { // simple pointer copy x := 42 From 9e9fb5e605b2c2c6632424c67702af686c6d58f7 Mon Sep 17 00:00:00 2001 From: Artemij Shepelev Date: Fri, 21 Sep 2018 17:14:22 +0300 Subject: [PATCH 2/3] changed readme with struct tags --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index f818418..e068f9c 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,18 @@ DeepCopy makes deep copies of things: unexported field values are not copied. ## Usage cpy := deepcopy.Copy(orig) + +## Tags + +The following struct tags is supported: + +``` +type A struct { + Field1 SomeType `deepcopy:"-"` // skip, will have zero-value in copy + Field2 *SomeType `deepcopy:"="` // treat like with "=" assignment operator +} +``` + +Specifically the `=` tag is usable when you want to copy a struct containing +something like `*sync.Mutex` or `*os.File` and don't want it to be deeply copied +but simply assigned. \ No newline at end of file From 9f995803ce53493ecd46b3cfc9f84f845d9013bd Mon Sep 17 00:00:00 2001 From: Artemij Shepelev Date: Wed, 7 Aug 2019 14:59:55 +0800 Subject: [PATCH 3/3] Converted to module-aware --- go.mod | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 go.mod diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..702b9d1 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/mtfelian/deepcopy + +go 1.12