package store import ( "database/sql" "fmt" sq "github.com/Masterminds/squirrel" "git.ctrlz.es/mgdelacroix/craban/model" ) var gameColumns = []string{"id", "user_id", "name"} var gameMemberColumns = []string{"game_id", "user_id", "role"} type GameStore struct { Conn *sql.DB } func NewGameStore(s *Store) *GameStore { return &GameStore{Conn: s.Conn} } func (gs *GameStore) Q() sq.StatementBuilderType { return sq.StatementBuilder.RunWith(gs.Conn) } func (gs *GameStore) gamesFromRows(rows *sql.Rows) ([]*model.Game, error) { games := []*model.Game{} for rows.Next() { var game model.Game err := rows.Scan( &game.ID, &game.UserID, &game.Name, ) if err != nil { return nil, err } games = append(games, &game) } return games, nil } func (gs *GameStore) getGameByCondition(condition sq.Eq) (*model.Game, error) { games, err := gs.getGamesByCondition(condition) if err != nil { return nil, err } return games[0], nil } func (gs *GameStore) getGamesByCondition(condition sq.Eq) ([]*model.Game, error) { rows, err := gs.Q().Select(gameColumns...). From("games"). Where(condition). Query() if err != nil { return nil, fmt.Errorf("cannot get games: %w", err) } defer rows.Close() games, err := gs.gamesFromRows(rows) if err != nil { return nil, fmt.Errorf("cannot get games from rows: %w", err) } if len(games) == 0 { return nil, sql.ErrNoRows } return games, nil } func (gs *GameStore) GetByID(id int) (*model.Game, error) { return gs.getGameByCondition(sq.Eq{"id": id}) } func (gs *GameStore) Create(name string, userID int) (*model.Game, error) { // ToDo: generate transaction // ToDo: add tests res, err := gs.Q().Insert("games"). Columns(gameColumns[1:]...). Values(userID, name). Exec() if err != nil { return nil, fmt.Errorf("cannot insert game: %w", err) } id, err := res.LastInsertId() if err != nil { return nil, fmt.Errorf("cannot get last insert id for created game: %w", err) } game, err := gs.GetByID(int(id)) if err != nil { return nil, fmt.Errorf("cannot get game by id: %w", err) } res, err = gs.Q().Insert("gamemembers"). Columns(gameMemberColumns...). Values(game.ID, userID, model.RoleGameMaster). Exec() if err != nil { return nil, fmt.Errorf("cannot insert gamemember for created game: %w", err) } return game, nil } func (gs *GameStore) gameMembersFromRows(rows *sql.Rows) ([]*model.GameMember, error) { gameMembers := []*model.GameMember{} for rows.Next() { var gameMember model.GameMember err := rows.Scan( &gameMember.GameID, &gameMember.UserID, &gameMember.Role, ) if err != nil { return nil, err } gameMembers = append(gameMembers, &gameMember) } return gameMembers, nil } func (gs *GameStore) GetMembers(gameID int) ([]*model.GameMember, error) { rows, err := gs.Q().Select(gameMemberColumns...). From("gamemembers"). Where(sq.Eq{"game_id": gameID}). Query() if err != nil { return nil, fmt.Errorf("cannot get game members: %w", err) } defer rows.Close() gameMembers, err := gs.gameMembersFromRows(rows) if err != nil { return nil, fmt.Errorf("cannot get game members from rows: %w", err) } if len(gameMembers) == 0 { return nil, sql.ErrNoRows } return gameMembers, nil } func (gs *GameStore) GetMember(gameID, userID int) (*model.GameMember, error) { rows, err := gs.Q().Select(gameMemberColumns...). From("gamemembers"). Where(sq.Eq{"game_id": gameID, "user_id": userID}). Query() if err != nil { return nil, fmt.Errorf("cannot get game member: %w", err) } defer rows.Close() gameMembers, err := gs.gameMembersFromRows(rows) if err != nil { return nil, fmt.Errorf("cannot get game member from rows: %w", err) } if len(gameMembers) == 0 { return nil, sql.ErrNoRows } return gameMembers[0], nil } func (gs *GameStore) AddMember(gameID, userID int, role string) (*model.GameMember, error) { _, err := gs.Q().Insert("gamemembers"). Columns(gameMemberColumns...). Values(gameID, userID, role). Exec() if err != nil { return nil, fmt.Errorf("cannot insert gamemember: %w", err) } return gs.GetMember(gameID, userID) } func (gs *GameStore) ListForUser(userID int) ([]*model.Game, error) { return gs.getGamesByCondition(sq.Eq{"user_id": userID}) } func (gs *GameStore) List() ([]*model.Game, error) { return gs.getGamesByCondition(sq.Eq{}) }