1use std::collections::{BTreeSet, HashMap};
2use std::path::PathBuf;
3
4use plexus_engine::{ExprKind, OpKind, Row, Value};
5
6#[derive(Debug, Clone, PartialEq)]
7pub struct ConformanceCase {
8 pub name: String,
9 pub query: String,
10 pub plan_mlir: Option<String>,
11 pub expected_rows: Vec<Row>,
12 pub expected_error_contains: Option<String>,
13 pub any_order: bool,
14 pub tags: Vec<String>,
15 pub setup: ConformanceSetup,
16}
17
18#[derive(Debug, Clone, PartialEq, Default)]
19pub struct ConformanceSetup {
20 pub graph_mode: Option<ConformanceGraphMode>,
21 pub setup_queries: Vec<String>,
22 pub seed_nodes: Vec<ConformanceSeedNode>,
23 pub seed_relationships: Vec<ConformanceSeedRelationship>,
24 pub assert_no_side_effects: bool,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum ConformanceGraphMode {
29 Empty,
30 Fixture,
31}
32
33#[derive(Debug, Clone, PartialEq)]
34pub struct ConformanceSeedNode {
35 pub id: u64,
36 pub labels: Vec<String>,
37 pub props: HashMap<String, Value>,
38}
39
40#[derive(Debug, Clone, PartialEq)]
41pub struct ConformanceSeedRelationship {
42 pub id: u64,
43 pub src: u64,
44 pub dst: u64,
45 pub typ: String,
46 pub props: HashMap<String, Value>,
47}
48
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub struct ConformanceFilterConfig {
51 pub include_untagged: bool,
52 pub allow_tags: Vec<String>,
53 pub deny_tags: Vec<String>,
54 pub deny_query_keywords: Vec<String>,
55}
56
57impl Default for ConformanceFilterConfig {
58 fn default() -> Self {
59 Self {
60 include_untagged: true,
61 allow_tags: vec![
62 "readonly".to_string(),
63 "read_only".to_string(),
64 "match".to_string(),
65 "return".to_string(),
66 "where".to_string(),
67 "with".to_string(),
68 "optional".to_string(),
69 "union".to_string(),
70 "unwind".to_string(),
71 ],
72 deny_tags: vec![
73 "write".to_string(),
74 "updating".to_string(),
75 "update".to_string(),
76 "sideeffect".to_string(),
77 "side_effect".to_string(),
78 "create".to_string(),
79 "merge".to_string(),
80 "delete".to_string(),
81 "set".to_string(),
82 "remove".to_string(),
83 ],
84 deny_query_keywords: vec![
85 " CREATE ".to_string(),
86 " MERGE ".to_string(),
87 " DELETE ".to_string(),
88 " DETACH DELETE ".to_string(),
89 " SET ".to_string(),
90 " REMOVE ".to_string(),
91 ],
92 }
93 }
94}
95
96#[derive(Debug, Clone, PartialEq, Eq, Default)]
97pub struct CorpusLoadOptions {
98 pub filter: Option<ConformanceFilterConfig>,
99 pub groups_csv: Option<String>,
100 pub group_map_path: Option<PathBuf>,
101}
102
103#[derive(Debug, PartialEq)]
104pub struct ConformanceRunCaseReport {
105 pub case: String,
106 pub tags: Vec<String>,
107 pub outcome: Result<(), ConformanceError>,
108}
109
110#[derive(Debug, PartialEq)]
111pub struct ConformanceRunReport {
112 pub total: usize,
113 pub passed: usize,
114 pub failed: usize,
115 pub cases: Vec<ConformanceRunCaseReport>,
116}
117
118#[derive(Debug, thiserror::Error, PartialEq)]
119pub enum ConformanceError {
120 #[error("case `{case}` expected error containing `{expected}` but succeeded")]
121 ExpectedErrorButSucceeded { case: String, expected: String },
122 #[error("case `{case}` expected success but failed with `{actual}`")]
123 ExpectedSuccessButFailed { case: String, actual: String },
124 #[error("case `{case}` expected error containing `{expected}`, got `{actual}`")]
125 ErrorMismatch {
126 case: String,
127 expected: String,
128 actual: String,
129 },
130 #[error("case `{case}` row mismatch (ordered)")]
131 RowMismatchOrdered {
132 case: String,
133 expected: Vec<Row>,
134 actual: Vec<Row>,
135 },
136 #[error("case `{case}` row mismatch (any-order)")]
137 RowMismatchAnyOrder {
138 case: String,
139 expected: Vec<Row>,
140 actual: Vec<Row>,
141 },
142}
143
144#[derive(Debug, thiserror::Error, PartialEq)]
145pub enum CorpusError {
146 #[error("I/O error: {0}")]
147 Io(String),
148 #[error("invalid corpus format in `{path}`: {message}")]
149 InvalidFormat { path: String, message: String },
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq)]
153pub enum MergeSortDir {
154 Asc,
155 Desc,
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
163pub enum ConformanceProfile {
164 CoreRead,
166 CoreWrite,
168 GqlSubset,
170 VectorExtension,
172}
173
174impl ConformanceProfile {
175 pub fn as_str(self) -> &'static str {
177 match self {
178 Self::CoreRead => "core-read",
179 Self::CoreWrite => "core-write",
180 Self::GqlSubset => "gql-subset",
181 Self::VectorExtension => "vector-extension",
182 }
183 }
184}
185
186pub struct ProfileSpec {
188 pub name: &'static str,
189 pub description: &'static str,
190 pub required_ops: BTreeSet<OpKind>,
191 pub required_exprs: BTreeSet<ExprKind>,
192 pub extends: Option<ConformanceProfile>,
194 pub graduation_criteria: &'static str,
196}
197
198#[derive(Debug, Clone, PartialEq, Eq)]
200pub struct ProfileValidationResult {
201 pub satisfied: bool,
203 pub missing_ops: BTreeSet<OpKind>,
205 pub missing_exprs: BTreeSet<ExprKind>,
207}