price-is-wrong/pkg/state/lobby/open.go

112 lines
2.6 KiB
Go

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
}