package orm import ( "fmt" "go.mongodb.org/mongo-driver/bson/primitive" "reflect" "regexp" "strconv" "strings" ) func panik(err error) { if err != nil { panic(err) } } func nameOf(i interface{}) string { v := reflect.ValueOf(i) var n string switch v.Kind() { case reflect.Slice, reflect.Map: if v.Type().Elem().Kind() == reflect.Pointer { n = v.Type().Elem().Elem().Name() } case reflect.Pointer: n = nameOf(reflect.Indirect(v).Interface()) default: n = v.Type().Name() } return n } func valueOf(i interface{}) reflect.Value { v := reflect.ValueOf(i) if v.Type().Kind() == reflect.Pointer { v = valueOf(reflect.Indirect(v).Interface()) } return v } func coerceInt(input reflect.Value, dst reflect.Value) interface{} { if input.Type().Kind() == reflect.Pointer { input = input.Elem() } if dst.Type().Kind() == reflect.Pointer { dst = dst.Elem() } if input.Type().ConvertibleTo(dst.Type()) { return input.Convert(dst.Type()).Interface() } return nil } var arrRegex, _ = regexp.Compile(`\[(?P\d+)]$`) func getNested(field string, aValue reflect.Value) (*reflect.Type, *reflect.Value, error) { if strings.HasPrefix(field, ".") || strings.HasSuffix(field, ".") { return nil, nil, fmt.Errorf(errFmtMalformedField, field) } value := aValue if value.Kind() == reflect.Pointer { value = value.Elem() } aft := value.Type() dots := strings.Split(field, ".") if value.Kind() != reflect.Struct { if value.Kind() == reflect.Slice { st := reflect.MakeSlice(value.Type().Elem(), 0, 0) for i := 0; i < value.Len(); i++ { cur := value.Index(i) if len(dots) > 1 { _, cv, _ := getNested(strings.Join(dots[1:], "."), cur.FieldByName(dots[0])) reflect.Append(st, *cv) //return getNested(, "."), fv) } else { reflect.Append(st, cur) } } typ := st.Type().Elem() return &typ, &st, nil } if len(dots) > 1 { return nil, nil, ErrNotSliceOrStruct } else { return &aft, &value, nil } } ref := value if ref.Kind() == reflect.Pointer { ref = ref.Elem() } var fv = ref.FieldByName(arrRegex.ReplaceAllString(dots[0], "")) if arrRegex.FindString(dots[0]) != "" && fv.Kind() == reflect.Slice { matches := arrRegex.FindStringSubmatch(dots[0]) ridx, _ := strconv.Atoi(matches[0]) idx := ridx fv = fv.Index(idx) } ft, _ := ref.Type().FieldByName(arrRegex.ReplaceAllString(dots[0], "")) if len(dots) > 1 { return getNested(strings.Join(dots[1:], "."), fv) } else { return &ft.Type, &fv, nil } } func makeSettable(rval reflect.Value, value interface{}) reflect.Value { if !rval.CanSet() { nv := reflect.New(rval.Type()) nv.Elem().Set(reflect.ValueOf(value)) return nv } return rval } func incrementInterface(t interface{}) interface{} { switch pt := t.(type) { case uint: t = pt + 1 case uint32: t = pt + 1 case uint64: t = pt + 1 case int: t = pt + 1 case int32: t = pt + 1 case int64: t = pt + 1 case string: t = NextStringID() case primitive.ObjectID: t = primitive.NewObjectID() default: panic(ErrUnsupportedID) } return t } func pull(s reflect.Value, idx int, typ reflect.Type) reflect.Value { retI := reflect.New(reflect.SliceOf(typ)) for i := 0; i < s.Len(); i++ { if i == idx { continue } retI.Elem().Set(reflect.Append(retI.Elem(), s.Index(i))) } return retI.Elem() } func checkStruct(ref reflect.Value) error { if ref.Kind() == reflect.Slice { return ErrAppendMultipleDocuments } if ref.Kind() != reflect.Struct { return ErrNotAStruct } return nil } func checkSlice(ref reflect.Value) error { if ref.Kind() != reflect.Slice { return ErrNotASlice } return nil } func convertSlice[In, Out any](in []In) []Out { out := make([]Out, 0) for _, i := range in { ii, ok := any(i).(Out) if ok { out = append(out, ii) } } return out } func normalizeSliceToDocumentSlice(in any) *DocumentSlice { ret := make(DocumentSlice, 0) val := reflect.ValueOf(in) if val.Kind() == reflect.Pointer { val = val.Elem() } if val.Kind() == reflect.Slice { for i := 0; i < val.Len(); i++ { if idoc, ok := val.Index(i).Interface().(IDocument); ok { ret = append(ret, idoc) } } } return &ret }