package orm import ( "database/sql" "errors" "fmt" "reflect" "github.com/go-pg/pg/v10/types" ) var errModelNil = errors.New("pg: Model(nil)") type useQueryOne interface { useQueryOne() bool } type HooklessModel interface { // Init is responsible to initialize/reset model state. // It is called only once no matter how many rows were returned. Init() error // NextColumnScanner returns a ColumnScanner that is used to scan columns // from the current row. It is called once for every row. NextColumnScanner() ColumnScanner // AddColumnScanner adds the ColumnScanner to the model. AddColumnScanner(ColumnScanner) error } type Model interface { HooklessModel AfterScanHook AfterSelectHook BeforeInsertHook AfterInsertHook BeforeUpdateHook AfterUpdateHook BeforeDeleteHook AfterDeleteHook } func NewModel(value interface{}) (Model, error) { return newModel(value, false) } func newScanModel(values []interface{}) (Model, error) { if len(values) > 1 { return Scan(values...), nil } return newModel(values[0], true) } func newModel(value interface{}, scan bool) (Model, error) { switch value := value.(type) { case Model: return value, nil case HooklessModel: return newModelWithHookStubs(value), nil case types.ValueScanner, sql.Scanner: if !scan { return nil, fmt.Errorf("pg: Model(unsupported %T)", value) } return Scan(value), nil } v := reflect.ValueOf(value) if !v.IsValid() { return nil, errModelNil } if v.Kind() != reflect.Ptr { return nil, fmt.Errorf("pg: Model(non-pointer %T)", value) } if v.IsNil() { typ := v.Type().Elem() if typ.Kind() == reflect.Struct { return newStructTableModel(GetTable(typ)), nil } return nil, errModelNil } v = v.Elem() if v.Kind() == reflect.Interface { if !v.IsNil() { v = v.Elem() if v.Kind() != reflect.Ptr { return nil, fmt.Errorf("pg: Model(non-pointer %s)", v.Type().String()) } } } switch v.Kind() { case reflect.Struct: if v.Type() != timeType { return newStructTableModelValue(v), nil } case reflect.Slice: elemType := sliceElemType(v) switch elemType.Kind() { case reflect.Struct: if elemType != timeType { return newSliceTableModel(v, elemType), nil } case reflect.Map: if err := validMap(elemType); err != nil { return nil, err } slicePtr := v.Addr().Interface().(*[]map[string]interface{}) return newMapSliceModel(slicePtr), nil } return newSliceModel(v, elemType), nil case reflect.Map: typ := v.Type() if err := validMap(typ); err != nil { return nil, err } mapPtr := v.Addr().Interface().(*map[string]interface{}) return newMapModel(mapPtr), nil } if !scan { return nil, fmt.Errorf("pg: Model(unsupported %T)", value) } return Scan(value), nil } type modelWithHookStubs struct { hookStubs HooklessModel } func newModelWithHookStubs(m HooklessModel) Model { return modelWithHookStubs{ HooklessModel: m, } } func validMap(typ reflect.Type) error { if typ.Key().Kind() != reflect.String || typ.Elem().Kind() != reflect.Interface { return fmt.Errorf("pg: Model(unsupported %s, expected *map[string]interface{})", typ.String()) } return nil }