package commons import ( "log" "reflect" "strings" "encoding/json" "github.com/go-viper/mapstructure/v2" "go.mongodb.org/mongo-driver/v2/bson" ) // StructHasStringValue given any struct will check if the requested property has // a non-blank value. func StructHasStringValue(o any, field string) bool { v := reflect.ValueOf(o).Elem().FieldByName(field) return v.String() != "" } // StructHasNotStringValue given any struct will check if the requested property has // a blank value. func StructHasNotStringValue(o any, field string) bool { return !StructHasStringValue(o, field) } // StructHasProperty cheks if a struct has the requested property. func StructHasProperty(value interface{}, name string) bool { vo := reflect.ValueOf(value).Elem() typeOfValue := vo.Type() has := false for i:= 0; i < vo.NumField(); i++ { if typeOfValue.Field(i).Name == name { has = true break } } return has } // StructMustTomap given any struct return the equivalent map[string]any or nil. // Will never throw. func StructMustToMap(data any) map[string]any { b, err := json.Marshal(data) if err != nil { log.Printf("Failed marshal %v", err) return nil } var res map[string]any err = json.Unmarshal(b, &res) if err != nil { log.Printf("Failed to unmarshal %v", err) return nil } return res } // StructSetValue will update the value of the given field of struct o. func StructSetValue(o any, field string, value any) { ref := reflect.ValueOf(o).Elem() if ref.Kind() == reflect.Ptr { ref = reflect.Indirect(ref) } if ref.Kind() == reflect.Interface { ref = ref.Elem() } if ref.Kind() == reflect.Struct { f := ref.FieldByName(field) if f.IsValid() && f.CanSet() { f.Set(reflect.ValueOf(value)) } } } func StructToStruct(source any, target any) { mapstructure.Decode(source, &target) } // StructToMapRecursive given a struct or a primitive will return the equivalent // map[string]any of the struct or the primitive as is. func StructToMapRecursive(obj any) any { v := reflect.ValueOf(obj) // Handle pointers by getting the underlying element if v.Kind() == reflect.Ptr { if v.IsNil() { return nil } v = v.Elem() } switch v.Kind() { case reflect.Struct: result := make(map[string]any) t := v.Type() for i := 0; i < v.NumField(); i++ { field := t.Field(i) // Skip unexported fields (private fields) if field.PkgPath != "" { continue } // Recurse into the field's value result[field.Name] = StructToMapRecursive(v.Field(i).Interface()) } return result case reflect.Slice, reflect.Array: result := make([]any, v.Len()) for i := 0; i < v.Len(); i++ { result[i] = StructToMapRecursive(v.Index(i).Interface()) } return result default: // Return basic types (int, string, etc.) as is return obj } } func BsonToStruct(m bson.M, o any) error { b, err := bson.Marshal(m) if err != nil { log.Printf("Failed marshal %v", err) return err } err = bson.Unmarshal(b, o) if err != nil { log.Printf("Failed to unmarshal %v", err) return err } return nil } // MapToStruct will convert a map[string]any to a struct. func MapToStruct(m map[string]any, o any) error { b, err := bson.Marshal(m) if err != nil { log.Printf("Failed marshal %v", err) return err } err = bson.Unmarshal(b, o) if err != nil { log.Printf("Failed to unmarshal %v", err) return err } return nil } func BsonToMap(b bson.M) map[string]any { result := make(map[string]any) for k, v := range b { switch val := v.(type) { case bson.M: result[k] = BsonToMap(val) case []interface{}: arr := make([]any, len(val)) for i, item := range val { if nested, ok := item.(bson.M); ok { arr[i] = BsonToMap(nested) } else { arr[i] = item } } result[k] = arr default: result[k] = val } } return result } type WrapA struct { Items []map[string]any } // BsonAToSlice will convert a bson.A to []map[string]any. func BsonAToSlice(m any) ([]map[string]any, error) { v := bson.M{ "items": m.(bson.A), } b, err := bson.Marshal(v) if err != nil { log.Printf("Failed marshal %v", err) return nil, err } var o WrapA err = bson.Unmarshal(b, &o) if err != nil { log.Printf("Failed to unmarshal %v", err) return nil, err } return o.Items, nil } // MapIsSubset given two map[string]any m1 and m2 will determine if m1 is a subset of m2. // Only fields' name is evaluated not their values. func MapIsSubset(subset, superset any) bool { sub := subset.(map[string]any) sup := superset.(map[string]any) isSubset := true for k, _ := range sub { _, ok := sup[k] if !ok { isSubset = false } } return isSubset } // MapIsSubsetOfStruct given a map[string]any and a struct will determine if the map is a subset of the struct. // Only fields' name is evaluated not their values. func MapIsSubsetOfStruct(m map[string]any, s any) bool { v := reflect.ValueOf(s) if v.Kind() == reflect.Ptr { v = v.Elem() // Dereference if it's a pointer } // Ensure we are working with a struct if v.Kind() != reflect.Struct { return false } isSubset := true t := v.Type() for key := range m { // FieldByName only finds exported fields if _, found := t.FieldByName(key); !found { isSubset = false break } } return isSubset } // StructHasJsonName determines if a struct has the given json tag name. func StructHasJsonName(s any, targetName string) bool { t := reflect.TypeOf(s) if t.Kind() == reflect.Ptr { t = t.Elem() } for i := 0; i < t.NumField(); i++ { field := t.Field(i) tag := field.Tag.Get("json") // The name is the first part before any commas (e.g., "user_id,omitempty") name := strings.Split(tag, ",")[0] if name == targetName { return true } } return false } // StructHasJsonName determines if a struct has the given bson tag name. func StructHasBsonName(s any, targetName string) bool { t := reflect.TypeOf(s) if t.Kind() == reflect.Ptr { t = t.Elem() } for i := 0; i < t.NumField(); i++ { field := t.Field(i) tag := field.Tag.Get("bson") // The name is the first part before any commas (e.g., "user_id,omitempty") name := strings.Split(tag, ",")[0] if name == targetName { return true } } return false } // StructCopyMatching copies the fields of one struct to another only if they have the same name and type. func StructCopyMatching(source, target any) { sVal := reflect.ValueOf(source).Elem() tVal := reflect.ValueOf(target).Elem() for i := 0; i < sVal.NumField(); i++ { sField := sVal.Type().Field(i) tField, ok := tVal.Type().FieldByName(sField.Name) if ok && sField.Type == tField.Type { tVal.FieldByName(sField.Name).Set(sVal.Field(i)) } } } // GetWithDefault func GetWithDefault(m map[string]any, path string, def any) any { keys := strings.Split(path, ".") var current any = m for _, k := range keys { if m2, ok := current.(map[string]any); ok { if val, exists := m2[k]; exists { current = val } else { return def } } else { return def } } return current } func GetStringWithDefault(m map[string]any, path, def string) string { a := GetWithDefault(m, path, def) return a.(string) } func GetString(m map[string]any, path string) string { a := Get(m, path) if a == nil { return "" } else { return a.(string) } } func Get(m map[string]any, path string) any { keys := strings.Split(path, ".") var current any = m for _, k := range keys { if m2, ok := current.(map[string]any); ok { if val, exists := m2[k]; exists { current = val } else { return nil } } else { return nil } } return current }