package workout import ( "database/sql/driver" "encoding/json" "fmt" "strings" "time" ) type Workout struct { ID uint `gorm:"primaryKey" json:"id"` UserID uint `gorm:"not null;index" json:"user_id"` Title string `gorm:"not null" json:"title"` Description string `gorm:"default:''" json:"description"` Type string `gorm:"default:''" json:"type"` Status string `gorm:"default:'planned'" json:"status"` ScheduledDate time.Time `gorm:"index" json:"scheduled_date"` Duration int `gorm:"default:0" json:"duration"` Distance float64 `gorm:"default:0" json:"distance"` ElevGain int `gorm:"default:0" json:"elev_gain"` AvgPower int `gorm:"default:0" json:"avg_power"` AvgHR int `gorm:"default:0" json:"avg_hr"` MaxPower int `gorm:"default:0" json:"max_power"` MaxHR int `gorm:"default:0" json:"max_hr"` CaloriesBurned int `gorm:"default:0" json:"calories_burned"` FileType string `gorm:"default:''" json:"file_type"` EquipmentID *uint `gorm:"index" json:"equipment_id"` FileURL string `gorm:"default:''" json:"file_url"` WorkoutData WorkoutDataJSON `gorm:"type:jsonb" json:"workout_data,omitempty"` RPE int `gorm:"default:0" json:"rpe"` Notes string `json:"notes"` Tags Tags `gorm:"type:jsonb;default:'[]'" json:"tags"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // Tags is a JSONB array of string tags for workouts type Tags []string // Scan implements the sql.Scanner interface for reading from the database func (t *Tags) Scan(value interface{}) error { if value == nil { *t = Tags{} return nil } switch v := value.(type) { case []byte: return json.Unmarshal(v, t) case string: return json.Unmarshal([]byte(v), t) default: return fmt.Errorf("unsupported type for Tags: %T", value) } } // Value implements the driver.Valuer interface for writing to the database func (t Tags) Value() (driver.Value, error) { if t == nil { return json.Marshal([]string{}) } return json.Marshal(t) } // String returns a comma-separated string representation func (t Tags) String() string { return strings.Join(t, ", ") } type WorkoutDataJSON struct { Name string `json:"name"` Author string `json:"author"` TotalDuration int `json:"total_duration"` Segments []WorkoutSegment `json:"segments"` } type WorkoutSegment struct { Type string `json:"type"` Duration int `json:"duration"` PowerLow float64 `json:"power_low"` PowerHigh float64 `json:"power_high"` Power float64 `json:"power"` Cadence int `json:"cadence"` } // Scan implements sql.Scanner interface func (w *WorkoutDataJSON) Scan(value interface{}) error { if value == nil { *w = WorkoutDataJSON{} return nil } bytes := value.([]byte) return json.Unmarshal(bytes, &w) } // Value implements driver.Valuer interface func (w WorkoutDataJSON) Value() (driver.Value, error) { return json.Marshal(w) } func (Workout) TableName() string { return "workouts" }