👁️
1import type { Deck, Section } from "@/lib/deck-types";
2import type {
3 Card,
4 ManaColor,
5 OracleId,
6 ScryfallId,
7} from "@/lib/scryfall-types";
8
9/**
10 * MTG Comprehensive Rules citation (e.g., "100.2a", "903.5c")
11 * Branded type for type safety
12 */
13export type RuleNumber = string & { readonly __brand: "RuleNumber" };
14
15export function asRuleNumber(rule: string): RuleNumber {
16 return rule as RuleNumber;
17}
18
19/**
20 * Categories for grouping related rules
21 */
22export type RuleCategory = "legality" | "quantity" | "identity" | "structure";
23
24/**
25 * Severity level for violations
26 */
27export type Severity = "error" | "warning";
28
29/**
30 * A single rule violation with context
31 */
32export interface Violation {
33 ruleId: string;
34 rule: RuleNumber;
35 category: RuleCategory;
36 cardName?: string;
37 oracleId?: OracleId;
38 section?: Section;
39 quantity?: number;
40 message: string;
41 severity: Severity;
42}
43
44/**
45 * Result of validating a deck
46 */
47export interface ValidationResult {
48 valid: boolean;
49 violations: Violation[];
50 byCard: Map<OracleId, Violation[]>;
51 byRule: Map<RuleNumber, Violation[]>;
52}
53
54/**
55 * Context passed to rule validators
56 */
57export interface ValidationContext {
58 deck: Deck;
59 cardLookup: (id: ScryfallId) => Card | undefined;
60 oracleLookup: (id: OracleId) => Card | undefined;
61 getPrintings: (id: OracleId) => Card[];
62 format: string | undefined;
63 commanderColors: ManaColor[] | undefined;
64 config: FormatConfig;
65}
66
67/**
68 * Per-format configuration parameters
69 */
70export interface FormatConfig {
71 legalityField?: string;
72 minDeckSize?: number;
73 deckSize?: number;
74 sideboardSize?: number;
75 /** Format supports alchemy (rebalanced A-) cards - Arena formats only */
76 supportsAlchemy?: boolean;
77}
78
79/**
80 * Rule definition
81 */
82export interface Rule<Id extends string = string> {
83 id: Id;
84 rule: RuleNumber;
85 ruleText?: string;
86 category: RuleCategory;
87 description: string;
88 validate: (ctx: ValidationContext) => Violation[];
89}
90
91/**
92 * Options for validation
93 */
94export interface ValidationOptions {
95 disabledRules?: Set<string>;
96 disabledCategories?: Set<RuleCategory>;
97 configOverrides?: Partial<FormatConfig>;
98 includeMaybeboard?: boolean;
99}
100
101/**
102 * Format preset combining rules and config
103 */
104export interface Preset<RuleId extends string = string> {
105 rules: readonly RuleId[];
106 config: FormatConfig;
107}
108
109/**
110 * Helper to create a violation
111 */
112export function violation(
113 rule: Rule,
114 message: string,
115 severity: Severity,
116 context?: {
117 cardName?: string;
118 oracleId?: OracleId;
119 section?: Section;
120 quantity?: number;
121 },
122): Violation {
123 return {
124 ruleId: rule.id,
125 rule: rule.rule,
126 category: rule.category,
127 message,
128 severity,
129 ...context,
130 };
131}