package storage import ( "context" "database/sql" "fmt" "training-tracker/internal/models" ) type ExerciseRepository struct { db *DB } func NewExerciseRepository(db *DB) *ExerciseRepository { return &ExerciseRepository{db: db} } func (r *ExerciseRepository) List(ctx context.Context) ([]models.Exercise, error) { query := `SELECT id, name, type, muscle_group, description, created_at FROM exercises ORDER BY name` rows, err := r.db.QueryContext(ctx, query) if err != nil { return nil, fmt.Errorf("failed to query exercises: %w", err) } defer rows.Close() var exercises []models.Exercise for rows.Next() { var e models.Exercise var muscleGroup, description sql.NullString if err := rows.Scan(&e.ID, &e.Name, &e.Type, &muscleGroup, &description, &e.CreatedAt); err != nil { return nil, fmt.Errorf("failed to scan exercise: %w", err) } e.MuscleGroup = muscleGroup.String e.Description = description.String exercises = append(exercises, e) } return exercises, nil } func (r *ExerciseRepository) GetByID(ctx context.Context, id int64) (*models.Exercise, error) { query := `SELECT id, name, type, muscle_group, description, created_at FROM exercises WHERE id = $1` var e models.Exercise var muscleGroup, description sql.NullString err := r.db.QueryRowContext(ctx, query, id).Scan(&e.ID, &e.Name, &e.Type, &muscleGroup, &description, &e.CreatedAt) if err == sql.ErrNoRows { return nil, nil } if err != nil { return nil, fmt.Errorf("failed to get exercise: %w", err) } e.MuscleGroup = muscleGroup.String e.Description = description.String return &e, nil } func (r *ExerciseRepository) Create(ctx context.Context, req *models.CreateExerciseRequest) (*models.Exercise, error) { query := `INSERT INTO exercises (name, type, muscle_group, description) VALUES ($1, $2, $3, $4) RETURNING id, name, type, muscle_group, description, created_at` var e models.Exercise var muscleGroup, description sql.NullString err := r.db.QueryRowContext(ctx, query, req.Name, req.Type, nullString(req.MuscleGroup), nullString(req.Description)). Scan(&e.ID, &e.Name, &e.Type, &muscleGroup, &description, &e.CreatedAt) if err != nil { return nil, fmt.Errorf("failed to create exercise: %w", err) } e.MuscleGroup = muscleGroup.String e.Description = description.String return &e, nil } func (r *ExerciseRepository) Update(ctx context.Context, id int64, req *models.CreateExerciseRequest) (*models.Exercise, error) { query := `UPDATE exercises SET name = $1, type = $2, muscle_group = $3, description = $4 WHERE id = $5 RETURNING id, name, type, muscle_group, description, created_at` var e models.Exercise var muscleGroup, description sql.NullString err := r.db.QueryRowContext(ctx, query, req.Name, req.Type, nullString(req.MuscleGroup), nullString(req.Description), id). Scan(&e.ID, &e.Name, &e.Type, &muscleGroup, &description, &e.CreatedAt) if err == sql.ErrNoRows { return nil, nil } if err != nil { return nil, fmt.Errorf("failed to update exercise: %w", err) } e.MuscleGroup = muscleGroup.String e.Description = description.String return &e, nil } func (r *ExerciseRepository) Delete(ctx context.Context, id int64) error { query := `DELETE FROM exercises WHERE id = $1` result, err := r.db.ExecContext(ctx, query, id) if err != nil { return fmt.Errorf("failed to delete exercise: %w", err) } rows, _ := result.RowsAffected() if rows == 0 { return sql.ErrNoRows } return nil } func nullString(s string) sql.NullString { if s == "" { return sql.NullString{} } return sql.NullString{String: s, Valid: true} }