package repository import ( "context" "errors" "log/slog" "github.com/gofrs/uuid/v5" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" ) var ( ErrNotFound error = errors.New("user cannot be found") ) type UserRow struct { Id uuid.UUID Email string HashedPassword string } func NewUserRow(id uuid.UUID, email string, hashedPassword string) UserRow { return UserRow{Id: id, Email: email, HashedPassword: hashedPassword} } func (u UserRow) Equal(user UserRow) bool { return u.Id == user.Id && u.Email == user.Email && u.HashedPassword == user.HashedPassword } type UserRepository struct { logger *slog.Logger db *pgxpool.Pool } func NewUserRepository(logger *slog.Logger, db *pgxpool.Pool) *UserRepository { return &UserRepository{ logger: logger, db: db, } } func (r *UserRepository) GetById(ctx context.Context, id uuid.UUID) (UserRow, error) { user := UserRow{} err := r.db.QueryRow(ctx, "SELECT * FROM users WHERE id = $1;", id).Scan(&user.Id, &user.Email, &user.HashedPassword) if err != nil { if err == pgx.ErrNoRows { return user, ErrNotFound } r.logger.ErrorContext(ctx, err.Error()) return user, err } return user, nil } func (r *UserRepository) Create(ctx context.Context, user UserRow) (UserRow, error) { var result pgx.Row if user.Id.IsNil() { result = r.db.QueryRow(ctx, "INSERT INTO users (email, hashed_password) VALUES ($1, $2) RETURNING id;", user.Email, user.HashedPassword) err := result.Scan(&user.Id) if err != nil { r.logger.ErrorContext(ctx, err.Error()) return UserRow{}, err } } else { _, err := r.db.Exec(ctx, "INSERT INTO users (id, email, hashed_password) VALUES ($1, $2, $3);", user.Id, user.Email, user.HashedPassword) if err != nil { r.logger.ErrorContext(ctx, err.Error()) return UserRow{}, err } } return user, nil } func (r *UserRepository) Update(ctx context.Context, user UserRow) error { result, err := r.db.Exec(ctx, "UPDATE users SET email = $1, hashed_password = $2 WHERE id = $3;", user.Email, user.HashedPassword, user.Id) if err != nil { r.logger.ErrorContext(ctx, err.Error()) return err } rowsAffected := result.RowsAffected() if rowsAffected == 0 { return ErrNotFound } return nil } func (r *UserRepository) Delete(ctx context.Context, id uuid.UUID) error { result, err := r.db.Exec(ctx, "DELETE FROM users WHERE id = $1;", id) if err != nil { r.logger.ErrorContext(ctx, err.Error()) return err } rowsAffected := result.RowsAffected() if rowsAffected == 0 { return ErrNotFound } return nil }