package repository import ( "database/sql" "errors" "fmt" models "vibeStonk/server/models/v1" ) var ( ErrUserNotFound = errors.New("user not found") ) func newSqliteUsersRepo(db *sql.DB) (UserRepo, error) { repo := &sqliteUsersRepo{db: db} if err := repo.initialize(); err != nil { return nil, err } return repo, nil } type sqliteUsersRepo struct { db *sql.DB } // initialize creates the users table if it doesn't exist func (s *sqliteUsersRepo) initialize() error { query := ` CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, username TEXT UNIQUE NOT NULL, pref_name TEXT, hash TEXT ); ` _, err := s.db.Exec(query) if err != nil { return fmt.Errorf("failed to create user table: %w", err) } query = ` CREATE INDEX IF NOT EXISTS users_usernames ON users(id, username); ` _, err = s.db.Exec(query) if err != nil { return fmt.Errorf("failed to create username index for users table: %w", err) } return err } func (s *sqliteUsersRepo) Create(user *models.User) (*models.User, error) { // Note: The hash field is in the proto definition but not in the generated struct // In a real implementation, we would need to handle this differently query := ` INSERT INTO users (id, username, pref_name, hash) VALUES (?, ?, ?, ?) ` _, err := s.db.Exec(query, user.Id, user.UserName, user.PrefName, user.Hash) if err != nil { return nil, err } return user, nil } func (s *sqliteUsersRepo) Get(id string) (*models.User, error) { query := ` SELECT id, username, pref_name, hash FROM users WHERE id = ? ` row := s.db.QueryRow(query, id) user := &models.User{} err := row.Scan(&user.Id, &user.UserName, &user.PrefName, &user.Hash) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, ErrUserNotFound } return nil, err } return user, nil } func (s *sqliteUsersRepo) GetByUsername(username string) (*models.User, error) { query := ` SELECT id, username, pref_name, hash FROM users WHERE username = ? ` row := s.db.QueryRow(query, username) user := &models.User{} err := row.Scan(&user.Id, &user.UserName, &user.PrefName, &user.Hash) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, ErrUserNotFound } return nil, err } return user, nil } func (s *sqliteUsersRepo) Update(user *models.User) error { // Note: The hash field is in the proto definition but not in the generated struct // In a real implementation, we would need to handle this differently query := ` UPDATE users SET username = ?, pref_name = ? WHERE id = ? ` result, err := s.db.Exec(query, user.UserName, user.PrefName, user.Id) if err != nil { return err } rowsAffected, err := result.RowsAffected() if err != nil { return err } if rowsAffected == 0 { return ErrUserNotFound } return nil } func (s *sqliteUsersRepo) Delete(user *models.User) error { query := ` DELETE FROM users WHERE id = ? ` result, err := s.db.Exec(query, user.Id) if err != nil { return err } rowsAffected, err := result.RowsAffected() if err != nil { return err } if rowsAffected == 0 { return ErrUserNotFound } return nil }