diff --git a/.gitignore b/.gitignore index 78fd378..3758ebf 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ **/*.sw* +/src/state/fsm/template diff --git a/src/state/fsm/.template/events.go b/src/state/fsm/.template/events.go new file mode 100644 index 0000000..1806416 --- /dev/null +++ b/src/state/fsm/.template/events.go @@ -0,0 +1,44 @@ +package template + +import ( + "context" + "gitea/price-is-wrong/src/lib/db" + template "gitea/price-is-wrong/src/state/fsm/template/internal" + "io" +) + +func (t *Template) TODO(ctx context.Context, id int) error { + return io.EOF +} + +func (t *Template) upsertEvent(ctx context.Context, e template.Event) error { + if err := upsertEvent(ctx, p.id, e); err != nil { + return err + } + + p2, err := openID(ctx, p.id) + if err != nil { + return err + } + + *p = *p2 + return nil +} + +func upsertEvent(ctx context.Context, templateID int, e template.Event) error { + b, err := template.MarshalEvent(e) + if err != nil { + return err + } + + _, err = db.From(ctx).ExecContext(ctx, ` + INSERT INTO template_events (template_id, payload) VALUES (?, ?) + `, templateID, b) + return err +} + +func (t *Template) apply(e template.Event) error { + switch e := e.(type) { + } + return nil +} diff --git a/src/state/fsm/.template/internal/event.go b/src/state/fsm/.template/internal/event.go new file mode 100644 index 0000000..651c9c5 --- /dev/null +++ b/src/state/fsm/.template/internal/event.go @@ -0,0 +1,20 @@ +package template + +import ( + "fmt" + "gitea/price-is-wrong/src/lib/event" +) + +type Event interface{} + +func ParseEvent(b []byte) (Event, error) { + typesToPointers := map[string]any{ + "*any": &b, + } + t, err := event.Parse(b, typesToPointers) + return typesToPointers[t], err +} + +func MarshalEvent(e Event) ([]byte, error) { + return event.Serialize(fmt.Sprintf("%T", e), e) +} diff --git a/src/state/fsm/.template/open.go b/src/state/fsm/.template/open.go new file mode 100644 index 0000000..de2e7f4 --- /dev/null +++ b/src/state/fsm/.template/open.go @@ -0,0 +1,115 @@ +package template + +import ( + "context" + "database/sql" + "fmt" + "gitea/price-is-wrong/src/lib/db" + template "gitea/price-is-wrong/src/state/fsm/template/internal" +) + +type Template struct { + id int + Host int + Contestants Player +} + +type Player struct { + ID int +} + +func Open(ctx context.Context, name string) (*Template, error) { + if err := initialize(ctx); err != nil { + return nil, fmt.Errorf("failed to initialize template: %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) (*Template, error) { + result, err := open(ctx, name) + if err != nil { + return nil, err + } + if result == nil { + return nil, fmt.Errorf("no template found with name %s", name) + } + return result, nil +} + +func open(ctx context.Context, name string) (*Template, error) { + row := db.From(ctx).QueryRow(`SELECT id FROM templates WHERE name=?`, name) + if err := row.Err(); err != nil { + return nil, fmt.Errorf("no template 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 templates: %w", err) + } + + return openID(ctx, int(id.Int32)) +} + +func openID(ctx context.Context, id int) (*Template, error) { + rows, err := db.From(ctx).QueryContext(ctx, ` + SELECT payload + FROM template_events + WHERE template_events.template_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 := Template{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 := template.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 templates (name) VALUES (?) + `, name) + return err +} + +func initialize(ctx context.Context) error { + _, err := db.From(ctx).ExecContext(ctx, ` + CREATE TABLE IF NOT EXISTS templates ( + id INTEGER PRIMARY KEY, + name TEXT + ); + CREATE TABLE IF NOT EXISTS template_events ( + id INTEGER PRIMARY KEY, + template_id NUMBER, + payload TEXT, + FOREIGN KEY (template_id) REFERENCES templates (id) + ); + `) + return err +} diff --git a/src/state/fsm/generate.go b/src/state/fsm/generate.go new file mode 100644 index 0000000..06bc261 --- /dev/null +++ b/src/state/fsm/generate.go @@ -0,0 +1,3 @@ +package fsm + +//go:generate cp -r ./.template ./template