/* * 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 14.01.2020 * */ package fields import ( "log" "reflect" "strings" "github.com/satori/go.uuid" ) //Convert copies field values from one struct to another based on tags setup func Сonvert(from interface{}, kind string, to interface{}) { if nil == from { log.Print("nil value is not supported") return } v := reflect.ValueOf(from) vt := reflect.ValueOf(to) t := v.Type() if t.Kind() != reflect.Struct { log.Printf("type %s is not supported", t.Kind()) return } if vt.Kind() == reflect.Ptr { vt = reflect.ValueOf(to).Elem() } for i := 0; i < t.NumField(); i++ { f := t.Field(i) // 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 != "" && f.Name == "" { continue } fv := v.Field(i) found, omit, omitempty := readTag(f, kind) // skip if tag "omit" set. if omit { continue } // skip empty values when "omitempty" set. if omitempty && fv.String() == "" { continue } if found { ft := vt.FieldByName(f.Name) if f.Name == strings.ToUpper(f.Name) && !ft.IsValid() { // was uppercase and not valid => to Capitalize first letter ft = vt.FieldByName(strings.Title(strings.ToLower(f.Name))) } if !ft.IsValid() && fv.Kind() == reflect.Struct { Сonvert(fv.Interface(), kind, to) } else { if ft.IsValid() { switch f.Type.String() { case "uuid.UUID": mv := fv.Interface().(uuid.UUID) if ft.Type().String() == "uuid.UUID" { ft.Set(fv) } else { ft.SetString(mv.String()) } default: ft.Set(fv) } } } } } } func readTag(f reflect.StructField, kind string) (bool, bool, bool) { val, ok := f.Tag.Lookup(ConvertTagName()) if !ok { return false, false, false } opts := strings.Split(val, ",") return indexOf(opts, kind) > -1, indexOf(opts, OmitTagName()) > -1, indexOf(opts, OmitEmptyTagName()) > -1 } func indexOf(a []string, x string) int { for i, n := range a { if x == n { return i } } return -1 }