From c08cbc36fca158870d0b60fbb04ebc38435daac2 Mon Sep 17 00:00:00 2001 From: Karl Matthias Date: Wed, 20 Nov 2024 11:31:40 +0000 Subject: [PATCH] Enable skipping unexported fields Co-Authored-By: Tom Patterer --- copier.go | 7 ++++++- copier_test.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/copier.go b/copier.go index 175ad82..79ef8ce 100644 --- a/copier.go +++ b/copier.go @@ -47,6 +47,9 @@ type Option struct { // Custom field name mappings to copy values with different names in `fromValue` and `toValue` types. // Examples can be found in `copier_field_name_mapping_test.go`. FieldNameMapping []FieldNameMapping + + // SkipUnexported will skip private fields: they will not be copied + SkipUnexported bool } func (opt Option) converters() map[converterPair]TypeConverter { @@ -320,7 +323,9 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error) // check source if source.IsValid() { - copyUnexportedStructFields(dest, source) + if !opt.SkipUnexported { + copyUnexportedStructFields(dest, source) + } // Copy from source field to dest field or method fromTypeFields := deepFields(fromType) diff --git a/copier_test.go b/copier_test.go index 1441df5..0627006 100644 --- a/copier_test.go +++ b/copier_test.go @@ -380,6 +380,10 @@ func TestStructField(t *testing.T) { DeepCopy: true, } + optionsSkipUnexported := copier.Option{ + SkipUnexported: true, + } + checkDetail := func(t *testing.T, source Detail, target Detail) { if source.Info1 != target.Info1 { t.Errorf("info1 is diff: source: %v, target: %v", source.Info1, target.Info1) @@ -704,6 +708,52 @@ func TestStructField(t *testing.T) { } }) }) + + t.Run("should handle unexported fields", func(t *testing.T) { + t.Run("should copy unexported fields", func(t *testing.T) { + from := &struct { + unexportedField string + }{ + unexportedField: "foo", + } + + to := &struct { + unexportedField string + }{} + + err := copier.Copy(to, from) + if err != nil { + t.Errorf("should not return an error") + return + } + + if to.unexportedField != from.unexportedField { + t.Errorf("should be equal") + } + }) + + t.Run("should not copy unexported fields with disallowed by the option", func(t *testing.T) { + from := &struct { + unexportedField string + }{ + unexportedField: "foo", + } + + to := &struct { + unexportedField string + }{} + + err := copier.CopyWithOption(to, from, optionsSkipUnexported) + if err != nil { + t.Errorf("should not return an error") + return + } + + if to.unexportedField == from.unexportedField { + t.Errorf("should not be equal") + } + }) + }) } func TestMapInterface(t *testing.T) {