diff --git a/copier.go b/copier.go index 175ad82..3e78f24 100644 --- a/copier.go +++ b/copier.go @@ -47,6 +47,11 @@ 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 + // When set to true, all fields will be treated as if they have the "must" tag + Must bool + // When set to true, all fields will be treated as if they have the "nopanic" tag. + // This only has an effect when Must is also true + NoPanic bool } func (opt Option) converters() map[converterPair]TypeConverter { @@ -312,8 +317,8 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error) dest = indirect(reflect.New(toType)) } - // Get tag options - flgs, err := getFlags(dest, source, toType, fromType) + var flgs flags + flgs, err = getFlags(dest, source, toType, fromType, opt) if err != nil { return err } @@ -732,7 +737,7 @@ func parseTags(tag string) (flg uint8, name string, err error) { } // getTagFlags Parses struct tags for bit flags, field name. -func getFlags(dest, src reflect.Value, toType, fromType reflect.Type) (flags, error) { +func getFlags(dest, src reflect.Value, toType, fromType reflect.Type, opt Option) (flags, error) { flgs := flags{ BitFlags: map[string]uint8{}, SrcNames: tagNameMapping{ @@ -745,6 +750,9 @@ func getFlags(dest, src reflect.Value, toType, fromType reflect.Type) (flags, er }, } + mustOpt := opt.Must + noPanicOpt := opt.NoPanic + var toTypeFields, fromTypeFields []reflect.StructField if dest.IsValid() { toTypeFields = deepFields(toType) @@ -766,6 +774,14 @@ func getFlags(dest, src reflect.Value, toType, fromType reflect.Type) (flags, er flgs.DestNames.TagToFieldName[name] = field.Name } } + + // Apply default flags + if mustOpt { + flgs.BitFlags[field.Name] = flgs.BitFlags[field.Name] | tagMust + } + if noPanicOpt { + flgs.BitFlags[field.Name] = flgs.BitFlags[field.Name] | tagNoPanic + } } // Get a list source of tags diff --git a/copier_tags_test.go b/copier_tags_test.go index 61ee238..4ec87c8 100644 --- a/copier_tags_test.go +++ b/copier_tags_test.go @@ -13,6 +13,20 @@ type EmployeeTags struct { ID int `copier:"-"` } +type EmployeeTags2 struct { + Name string `copier:"must,nopanic"` + DOB string + Address string + ID int `copier:"-"` +} + +type EmployeeTags3 struct { + Name string + DOB string + Address string + ID int +} + type User1 struct { Name string DOB string @@ -49,6 +63,46 @@ func TestCopyTagMust(t *testing.T) { copier.Copy(employee, user) } +func TestCopyTagMustByOption(t *testing.T) { + employee := &EmployeeTags3{} + user := &User2{DOB: "1 January 1970"} + t.Run("must is true", func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("Expected a panic.") + } + }() + copier.CopyWithOption(employee, user, copier.Option{Must: true}) + }) + + t.Run("must is false", func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Error("Expected no panic.") + } + }() + copier.CopyWithOption(employee, user, copier.Option{Must: false}) + }) +} + +func TestCopyTagMustAndNoPanic(t *testing.T) { + employee := &EmployeeTags2{} + user := &User2{DOB: "1 January 1970"} + err := copier.Copy(employee, user) + if err == nil { + t.Error("expected error") + } +} + +func TestCopyTagMustAndNoPanicByOption(t *testing.T) { + employee := &EmployeeTags3{} + user := &User2{DOB: "1 January 1970"} + err := copier.CopyWithOption(employee, user, copier.Option{Must: true, NoPanic: true}) + if err == nil { + t.Error("expected error") + } +} + func TestCopyTagOverrideZeroValue(t *testing.T) { options := copier.Option{IgnoreEmpty: true} employee := EmployeeTags{ID: 100, Address: ""}