package lobby import ( "context" "database/sql" "fmt" "gitea/price-is-wrong/pkg/lib/db" lobby "gitea/price-is-wrong/pkg/state/lobby/internal" ) type Lobby struct { id int Players []int Closed bool } func Open(ctx context.Context, name string) (*Lobby, error) { if err := initialize(ctx); err != nil { return nil, fmt.Errorf("failed to initialize lobbies: %w", err) } if result, err := open(ctx, name); err != nil { return nil, err } else if result == nil { if err := create(ctx, name); err != nil { return nil, err } } return mustOpen(ctx, name) } func mustOpen(ctx context.Context, name string) (*Lobby, error) { result, err := open(ctx, name) if err != nil { return nil, err } if result == nil { return nil, fmt.Errorf("no lobby found with name %s", name) } return result, nil } func open(ctx context.Context, name string) (*Lobby, error) { row := db.From(ctx).QueryRow(`SELECT id FROM lobbies WHERE name=?`, name) if err := row.Err(); err != nil { return nil, fmt.Errorf("no lobby found with name %s", name) } var id sql.NullInt32 if err := row.Scan(&id); err == sql.ErrNoRows || !id.Valid { return nil, nil } else if err != nil { return nil, fmt.Errorf("failed to scan id from lobby: %w", err) } return openID(ctx, int(id.Int32)) } func openID(ctx context.Context, id int) (*Lobby, error) { rows, err := db.From(ctx).QueryContext(ctx, ` SELECT payload FROM lobby_events WHERE lobby_events.lobby_id=? ORDER BY id `, id) if err != nil { return nil, fmt.Errorf("failed to query event payloads for id %d: %w", id, err) } defer rows.Close() result := Lobby{id: id} for rows.Next() { var b []byte if err := rows.Scan(&b); err != nil { return nil, fmt.Errorf("failed to scan event: %w", err) } event, err := lobby.ParseEvent(b) if err != nil { return nil, fmt.Errorf("failed to parse event: %w", err) } if err := result.apply(event); err != nil { return nil, fmt.Errorf("failed to apply event %s: %w", b, err) } } return &result, rows.Err() } func create(ctx context.Context, name string) error { _, err := db.From(ctx).ExecContext(ctx, ` INSERT INTO lobbies (name) VALUES (?) `, name) return err } func initialize(ctx context.Context) error { _, err := db.From(ctx).ExecContext(ctx, ` CREATE TABLE IF NOT EXISTS lobbies ( id INTEGER PRIMARY KEY, name TEXT ); CREATE TABLE IF NOT EXISTS lobby_events ( id INTEGER PRIMARY KEY, lobby_id NUMBER, payload TEXT, FOREIGN KEY (lobby_id) REFERENCES lobbies (id) ); `) return err }