package crud import ( "context" "database/sql" "fmt" "git.nspix.com/golang/rest/orm/schema" "gorm.io/gorm" "reflect" "strconv" "sync" "time" ) var ( DefaultFormat = NewFormatter() ) type HandleFunc func(ctx context.Context, value interface{}, model interface{}, scm *schema.Schema) interface{} type Formatter struct { ctx context.Context locker sync.RWMutex formats map[string]HandleFunc } func stringFormat(ctx context.Context, value interface{}, model interface{}, scm *schema.Schema) interface{} { return fmt.Sprint(value) } func integerFormat(ctx context.Context, value interface{}, model interface{}, scm *schema.Schema) interface{} { var ( n int ) switch value.(type) { case float32, float64: n = int(reflect.ValueOf(value).Float()) case int, int8, int16, int32, int64: n = int(reflect.ValueOf(value).Int()) case uint, uint8, uint16, uint32, uint64: n = int(reflect.ValueOf(value).Uint()) case string: n, _ = strconv.Atoi(reflect.ValueOf(value).String()) case []byte: n, _ = strconv.Atoi(string(reflect.ValueOf(value).Bytes())) } return n } func decimalFormat(ctx context.Context, value interface{}, model interface{}, scm *schema.Schema) interface{} { var ( n float64 ) switch value.(type) { case float32, float64: n = float64(reflect.ValueOf(value).Float()) case int, int8, int16, int32, int64: n = float64(reflect.ValueOf(value).Int()) case uint, uint8, uint16, uint32, uint64: n = float64(reflect.ValueOf(value).Uint()) case string: n, _ = strconv.ParseFloat(reflect.ValueOf(value).String(), 64) case []byte: n, _ = strconv.ParseFloat(string(reflect.ValueOf(value).Bytes()), 64) } return n } func dateFormat(ctx context.Context, value interface{}, model interface{}, scm *schema.Schema) interface{} { if t, ok := value.(time.Time); ok { return t.Format("2006-01-02") } if t, ok := value.(*sql.NullTime); ok { if t != nil && t.Valid { return t.Time.Format("2006-01-02") } } return "" } func timeFormat(ctx context.Context, value interface{}, model interface{}, scm *schema.Schema) interface{} { if t, ok := value.(time.Time); ok { return t.Format("15:04:05") } if t, ok := value.(*sql.NullTime); ok { if t != nil && t.Valid { return t.Time.Format("15:04:05") } } return "" } func datetimeFormat(ctx context.Context, value interface{}, model interface{}, scm *schema.Schema) interface{} { if t, ok := value.(time.Time); ok { return t.Format("2006-01-02 15:04:05") } if t, ok := value.(*sql.NullTime); ok { if t != nil && t.Valid { return t.Time.Format("2006-01-02 15:04:05") } } return "" } func durationFormat(ctx context.Context, value interface{}, model interface{}, scm *schema.Schema) interface{} { var ( hour int min int sec int ) n := integerFormat(ctx, value, model, scm).(int) hour = n / 3600 min = (n - hour*3600) / 60 sec = n - hour*3600 - min*60 return fmt.Sprintf("%02d:%02d:%02d", hour, min, sec) } func dropdownFormat(ctx context.Context, value interface{}, model interface{}, scm *schema.Schema) interface{} { opts := scm.GetOptions() if opts != nil && opts.Items != nil { for _, v := range opts.Items { if v.Key == value { return v.Val } } } return value } func (format *Formatter) Register(f string, fun HandleFunc) { format.locker.Lock() format.formats[f] = fun format.locker.Unlock() } func (format *Formatter) Format(ft string, value interface{}, model interface{}, scm *schema.Schema) interface{} { format.locker.RLock() f, ok := format.formats[ft] format.locker.RUnlock() if ok { return f(format.ctx, value, model, scm) } return value } func (format *Formatter) formatSchema(val interface{}, schemas []*schema.Schema, stmt *gorm.Statement) interface{} { values := make(map[string]interface{}) refVal := reflect.Indirect(reflect.ValueOf(val)) for _, field := range schemas { f := stmt.Schema.LookUpField(field.Column) values[field.Column] = format.Format(field.Format, refVal.FieldByName(f.Name).Interface(), val, field) } return values } func (format *Formatter) formatSchemas(val interface{}, schemas []*schema.Schema, stmt *gorm.Statement) interface{} { reflectValue := reflect.Indirect(reflect.ValueOf(val)) if reflectValue.Type().Kind() != reflect.Slice { return nil } length := reflectValue.Len() values := make([]interface{}, length) for i := 0; i < length; i++ { values[i] = format.formatSchema(reflectValue.Index(i).Interface(), schemas, stmt) } return values } func NewFormatter() *Formatter { f := &Formatter{ ctx: context.Background(), formats: make(map[string]HandleFunc), } f.Register("string", stringFormat) f.Register("integer", integerFormat) f.Register("decimal", decimalFormat) f.Register("date", dateFormat) f.Register("time", timeFormat) f.Register("datetime", datetimeFormat) f.Register("duration", durationFormat) f.Register("dropdown", dropdownFormat) return f }