package themis import ( "context" "database/sql" _ "embed" "fmt" _ "github.com/mattn/go-sqlite3" ) //go:embed migrations/init.sql var initScript string const ( CLAIM_TYPE_AREA = iota CLAIM_TYPE_REGION CLAIM_TYPE_TRADE ) var claimTypeEnum = map[string]int{ "area": CLAIM_TYPE_AREA, "region": CLAIM_TYPE_REGION, "trade": CLAIM_TYPE_TRADE, } var claimTypeEnumVals = map[int]string{ CLAIM_TYPE_AREA: "area", CLAIM_TYPE_REGION: "region", CLAIM_TYPE_TRADE: "trade", } var claimTypeToColumn = map[string]string{ "area": "area", "region": "region", "trade": "trade_node", } type Store struct { db *sql.DB } func NewStore(conn string) (*Store, error) { db, err := sql.Open("sqlite3", conn) if err != nil { return nil, fmt.Errorf("failed to open database: %w", err) } _, err = db.Exec(initScript) if err != nil { return nil, fmt.Errorf("failed to run init script: %w", err) } return &Store{ db: db, }, nil } func (s *Store) Claim(ctx context.Context, player, province string, typ int) error { tx, err := s.db.Begin() if err != nil { return fmt.Errorf("failed to begin transaction: %w", err) } defer tx.Commit() // Check conflicts stmt, err := s.db.PrepareContext(ctx, fmt.Sprintf(`SELECT COUNT(1) FROM provinces WHERE provinces.%s = ? and provinces.name in ( SELECT provinces.name FROM claims LEFT JOIN provinces ON claims.val = provinces.trade_node WHERE claims.claim_type = 'trade' UNION SELECT provinces.name from claims LEFT JOIN provinces ON claims.val = provinces.region WHERE claims.claim_type = 'region' UNION SELECT provinces.name from claims LEFT JOIN provinces ON claims.val = provinces.area WHERE claims.claim_type = 'area' )`, claimTypeToColumn[claimTypeEnumVals[typ]])) if err != nil { return fmt.Errorf("failed to prepare conflicts query: %w", err) } row := stmt.QueryRowContext(ctx, province) var count int err = row.Scan(&count) if err != nil { return fmt.Errorf("failed to get count of conflicting provinces: %w", err) } if count > 0 { return fmt.Errorf("found %d conflicting provinces", count) } stmt, err = s.db.PrepareContext(ctx, "INSERT INTO claims (player, claim_type, val) VALUES (?, ?, ?)") if err != nil { return fmt.Errorf("failed to prepare claim query: %w", err) } _, err = stmt.ExecContext(ctx, player, claimTypeEnumVals[typ], province) if err != nil { return fmt.Errorf("failed to insert claim: %w", err) } return nil }