119 lines
2.8 KiB
Go
119 lines
2.8 KiB
Go
package priceiswrong
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"gitea/price-is-wrong/src/lib/db"
|
|
priceiswrong "gitea/price-is-wrong/src/state/priceiswrong/internal"
|
|
)
|
|
|
|
type PriceIsWrong struct {
|
|
id int
|
|
Host int
|
|
Contestants []Player
|
|
Item priceiswrong.Item
|
|
}
|
|
|
|
type Player struct {
|
|
ID int
|
|
Score int
|
|
Guess string
|
|
}
|
|
|
|
func Open(ctx context.Context, name string) (*PriceIsWrong, error) {
|
|
if err := initialize(ctx); err != nil {
|
|
return nil, fmt.Errorf("failed to initialize priceiswrong: %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) (*PriceIsWrong, error) {
|
|
result, err := open(ctx, name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if result == nil {
|
|
return nil, fmt.Errorf("no priceiswrong found with name %s", name)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func open(ctx context.Context, name string) (*PriceIsWrong, error) {
|
|
row := db.From(ctx).QueryRow(`SELECT id FROM priceiswrongs WHERE name=?`, name)
|
|
if err := row.Err(); err != nil {
|
|
return nil, fmt.Errorf("no priceiswrong 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 priceiswrongs: %w", err)
|
|
}
|
|
|
|
return openID(ctx, int(id.Int32))
|
|
}
|
|
|
|
func openID(ctx context.Context, id int) (*PriceIsWrong, error) {
|
|
rows, err := db.From(ctx).QueryContext(ctx, `
|
|
SELECT payload
|
|
FROM priceiswrong_events
|
|
WHERE priceiswrong_events.priceiswrong_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 := PriceIsWrong{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 := priceiswrong.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 priceiswrongs (name) VALUES (?)
|
|
`, name)
|
|
return err
|
|
}
|
|
|
|
func initialize(ctx context.Context) error {
|
|
_, err := db.From(ctx).ExecContext(ctx, `
|
|
CREATE TABLE IF NOT EXISTS priceiswrongs (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT
|
|
);
|
|
CREATE TABLE IF NOT EXISTS priceiswrong_events (
|
|
id INTEGER PRIMARY KEY,
|
|
priceiswrong_id NUMBER,
|
|
payload TEXT,
|
|
FOREIGN KEY (priceiswrong_id) REFERENCES priceiswrongs (id)
|
|
);
|
|
`)
|
|
return err
|
|
}
|