diff --git a/app/main.go b/app/main.go index 095201e..ad9e728 100644 --- a/app/main.go +++ b/app/main.go @@ -7,7 +7,9 @@ import ( "repo-pattern/app/lib/logging" "repo-pattern/app/models" "repo-pattern/app/repository" + "repo-pattern/app/repository/smartfilter" + "github.com/google/uuid" "gorm.io/gorm" ) @@ -20,8 +22,11 @@ func doMagic(db *gorm.DB) { var err error query := db - f := repository.SmartCertFilter[models.Cert]{ + id, _ := uuid.FromBytes([]byte("6dc096ab-5c03-427e-b808-c669f7446131")) + + f := smartfilter.SmartCertFilter[models.Cert]{ Alive: &TRUE, + Id: &id, } query, err = f.ToQuery(query) diff --git a/app/repository/smartfilter/filterfield.go b/app/repository/smartfilter/filterfield.go new file mode 100644 index 0000000..73b4878 --- /dev/null +++ b/app/repository/smartfilter/filterfield.go @@ -0,0 +1,105 @@ +package smartfilter + +import ( + "fmt" + "reflect" + + "github.com/google/uuid" +) + +type valueGetterFunc func(ff *FilterField, v reflect.Value) error + +type FilterField struct { + Name string + Operator Operator + + boolValue bool + intValue int64 + uintValue uint64 + floatValue float64 + strValue string +} + +func boolValueGetter(ff *FilterField, v reflect.Value) error { + ff.boolValue = v.Bool() + return nil +} + +func intValueGetter(ff *FilterField, v reflect.Value) error { + ff.intValue = v.Int() + return nil +} + +func uintValueGetter(ff *FilterField, v reflect.Value) error { + ff.uintValue = v.Uint() + return nil +} + +func floatValueGetter(ff *FilterField, v reflect.Value) error { + ff.floatValue = v.Float() + return nil +} + +func strValueGetter(ff *FilterField, v reflect.Value) error { + ff.strValue = v.String() + return nil +} + +func uuidValueGetter(ff *FilterField, v reflect.Value) error { + uid, err := uuid.FromBytes([]byte(v.String())) + if err != nil { + return err + } + ff.strValue = uid.String() + return nil +} + +func unsupportedValueGetter(ff *FilterField, v reflect.Value) error { + return fmt.Errorf("unsupported type: %v", v.Type()) +} + +func newTypeGetter(t reflect.Type, allowAddr bool) valueGetterFunc { + // If we have a non-pointer value whose type implements + // Marshaler with a value receiver, then we're better off taking + // the address of the value - otherwise we end up with an + // allocation as we cast the value to an interface. + // if t.Kind() != reflect.Pointer && allowAddr && reflect.PointerTo(t).Implements(marshalerType) { + // return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false)) + // } + // if t.Implements(marshalerType) { + // return marshalerEncoder + // } + // if t.Kind() != reflect.Pointer && allowAddr && reflect.PointerTo(t).Implements(textMarshalerType) { + // return newCondAddrEncoder(addrTextMarshalerEncoder, newTypeEncoder(t, false)) + // } + // if t.Implements(textMarshalerType) { + // return textMarshalerEncoder + // } + + switch t.Kind() { + case reflect.Bool: + return boolValueGetter + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return intValueGetter + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return uintValueGetter + case reflect.Float32, reflect.Float64: + return floatValueGetter + case reflect.String: + return strValueGetter + // case reflect.Interface: + // return interfaceEncoder + // case reflect.Struct: + // return newStructEncoder(t) + // case reflect.Map: + // return newMapEncoder(t) + // case reflect.Slice: + // return newSliceEncoder(t) + // case reflect.Array: + // return newArrayEncoder(t) + // case reflect.Pointer: + // return newPtrEncoder(t) + default: + return unsupportedValueGetter + } +} diff --git a/app/repository/smartfilter.go b/app/repository/smartfilter/smartfilter.go similarity index 69% rename from app/repository/smartfilter.go rename to app/repository/smartfilter/smartfilter.go index 266e2e0..c076bfb 100644 --- a/app/repository/smartfilter.go +++ b/app/repository/smartfilter/smartfilter.go @@ -1,4 +1,4 @@ -package repository +package smartfilter import ( "fmt" @@ -23,11 +23,6 @@ const ( var OPERATORS = []Operator{OperatorEQ, OperatorIN} -type FilterField struct { - Name string - Operator Operator -} - type SmartCertFilter[T schema.Tabler] struct { Model T Alive *bool `filterfield:"alive,EQ"` @@ -40,33 +35,46 @@ func (f SmartCertFilter[T]) ToQuery(query *gorm.DB) (*gorm.DB, error) { tableName := f.Model.TableName() fmt.Printf("Table name: %s\n", tableName) - fmt.Printf("%+v\n", f) + // fmt.Printf("%+v\n", f) st := reflect.TypeOf(f) modelName := st.Name() + reflectValue := reflect.ValueOf(f) for i := 0; i < st.NumField(); i++ { field := st.Field(i) tagValue := field.Tag.Get(TAG_NAME) + + // skip field if filter tag is not present if len(tagValue) == 0 { continue } + fieldReflect := reflectValue.FieldByName(field.Name) + + // skip field if value is nil + if fieldReflect.IsNil() { + continue + } + + t := fieldReflect.Type() + fmt.Printf(">>> %+v --- %+v\n", field, t) + filterField, err := getFilterField(tagValue) if err != nil { return nil, fmt.Errorf("%s.%s: %s", modelName, field.Name, err) } - fmt.Printf( - "tagValue: %s, Name: %s, Operator: %s\n", - tagValue, - filterField.Name, - filterField.Operator, - ) + // fmt.Printf( + // "tagValue: %s, Name: %s, Operator: %s\n", + // tagValue, + // filterField.Name, + // filterField.Operator, + // ) switch filterField.Operator { case OperatorEQ: - query = applyFilterEQ(query, tableName, filterField) + query = applyFilterEQ[string](query, tableName, filterField) } } @@ -75,7 +83,9 @@ func (f SmartCertFilter[T]) ToQuery(query *gorm.DB) (*gorm.DB, error) { return query, nil } -func applyFilterEQ(query *gorm.DB, tableName string, filterField *FilterField) *gorm.DB { +func applyFilterEQ[T int | bool | string](query *gorm.DB, tableName string, filterField *FilterField) *gorm.DB { + // query = query.Where(fmt.Sprint("%s.%s = ?", tableName, filterField.Name), ) + return query }