diff --git a/examples/example-3/main.go b/examples/example-3/main.go new file mode 100644 index 0000000..352fd5a --- /dev/null +++ b/examples/example-3/main.go @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2020 Alex aka mailoman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Alex aka mailoman + * @copyright Copyright (c) 2020 Alex aka mailoman + * @since 26.01.2020 + * + */ + +package main + +import ( + "encoding/json" + "log" + + "github.com/go-convert/fields" +) + +type FromStructBase struct { + ID string `conv:"new,update"` + Field1 string `conv:"new,update"` +} + +type fromStruct struct { + FromStructBase `conv:"new,update"` + Field2 string `conv:"update"` +} + +type toStruct struct { + ID string + Field1 string + Field2 string + Field3 string +} + +func main() { + fromDefault := fromStruct{ + FromStructBase{"1", "field 1 value"}, "field 2 value", + } + toDefault := toStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", + } + + log.Print("example 3.1 - new") + from := fromDefault + to := toDefault + + fields.Сonvert(from, "new", &to) + fromB, _ := json.Marshal(from) + toB, _ := json.Marshal(to) + + log.Printf("from %s => to %s", string(fromB), string(toB)) + + log.Print("example 3.2 - update") + from = fromDefault + to = toDefault + + fields.Сonvert(from, "update", &to) + fromB, _ = json.Marshal(from) + toB, _ = json.Marshal(to) + + log.Printf("from %s => to %s", string(fromB), string(toB)) +} diff --git a/fields.go b/fields.go index 959ccce..3e4cc5d 100644 --- a/fields.go +++ b/fields.go @@ -54,7 +54,7 @@ func Сonvert(from interface{}, kind string, to interface{}) { // skip unexported fields. from godoc: // PkgPath is the package path that qualifies a lower case (unexported) // field name. It is empty for upper case (exported) field names. - if f.PkgPath != "" { + if f.PkgPath != "" && f.Name == "" { continue } fv := v.Field(i) @@ -70,7 +70,11 @@ func Сonvert(from interface{}, kind string, to interface{}) { if found { ft := vt.FieldByName(f.Name) - ft.Set(fv) + if !ft.IsValid() && fv.Kind() == reflect.Struct { + Сonvert(fv.Interface(), kind, to) + } else { + ft.Set(fv) + } } } } diff --git a/fields_test.go b/fields_test.go index 2c41405..f760fdd 100644 --- a/fields_test.go +++ b/fields_test.go @@ -59,6 +59,18 @@ type FromStructCustom struct { Field4 string `copy:"category-1,noempty"` } +type FromStructBase struct { + ID string `conv:"new,update"` + Field1 string `conv:"new,update"` +} + +type FromStructCombined struct { + FromStructBase `conv:"new,update"` + Field2 string `conv:"update"` + Field3 string `conv:"update,omit"` + Field4 string `conv:"category-1,omitempty"` +} + var ( // testsNormal provides normal test cases testsNormal = []struct { @@ -258,6 +270,67 @@ var ( }, }, } + + // testsCombinedNormal provides normal test cases with custom init config + testsCombinedNormal = []struct { + from FromStructCombined + kind string + to ToStruct + expectedTo ToStruct + }{ + // case 3.1 + { + FromStructCombined{ + FromStructBase{"1", "field 1 value"}, "field 2 value", "omitting", "", + }, + "new", + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "", + }, + ToStruct{ + "1", "field 1 value", "old field 2 value", "old field 3 value", "", + }, + }, + // case 3.2 + { + FromStructCombined{ + FromStructBase{"1", "field 1 value"}, "field 2 value", "omitting", "", + }, + "update", + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "", + }, + ToStruct{ + "1", "field 1 value", "field 2 value", "old field 3 value", "", + }, + }, + // case 3.3.1 + { + FromStructCombined{ + FromStructBase{"1", "field 1 value"}, "field 2 value", "omitting", "", + }, + "category-1", + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "old field 4 value", + }, + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "old field 4 value", + }, + }, + // case 3.3.2 + { + FromStructCombined{ + FromStructBase{"1", "field 1 value"}, "field 2 value", "omitting", "not empty", + }, + "category-1", + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "old field 4 value", + }, + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "not empty", + }, + }, + } ) func TestConvert_Normal(t *testing.T) { @@ -357,3 +430,17 @@ func TestOmitEmptyTagName(t *testing.T) { assert.Equal(t, newValue, fields.OmitEmptyTagName()) } + +func TestConvert_CombinedNormal(t *testing.T) { + fields.SetDefaults() + for _, test := range testsCombinedNormal { + + to := &ToStruct{} + *to = test.to + + fields.Сonvert(test.from, test.kind, to) + + assert.NotEmpty(t, *to) + assert.Equal(t, test.expectedTo, *to) + } +}