Skip to main content

plexus_conformance/
types.rs

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/// A named conformance profile representing a defined subset of Plexus op/expr support.
159///
160/// Engines declare conformance by calling [`crate::validate_for_profile`] with their
161/// [`plexus_engine::EngineCapabilities`] and a `ConformanceProfile` value.
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
163pub enum ConformanceProfile {
164    /// Read-only core: MATCH/WHERE/RETURN/WITH/ORDER BY/LIMIT/SKIP and Phase 1 expressions.
165    CoreRead,
166    /// Everything in `CoreRead` plus DML mutation ops (CREATE/MERGE/DELETE/SET/REMOVE).
167    CoreWrite,
168    /// Everything in `CoreRead` plus Phase 10 GQL-specific ops and Phase 2 expression forms.
169    GqlSubset,
170    /// Everything in `CoreRead` plus vector/hybrid retrieval ops and VectorSimilarity expressions.
171    VectorExtension,
172}
173
174impl ConformanceProfile {
175    /// Human-readable name for use in diagnostics and documentation.
176    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
186/// The op/expr requirements for a [`ConformanceProfile`].
187pub 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    /// The profile this one extends, if any (informational).
193    pub extends: Option<ConformanceProfile>,
194    /// Graduation criteria prose.
195    pub graduation_criteria: &'static str,
196}
197
198/// Result of validating an engine's capabilities against a [`ConformanceProfile`].
199#[derive(Debug, Clone, PartialEq, Eq)]
200pub struct ProfileValidationResult {
201    /// Whether the engine's capabilities fully satisfy the profile.
202    pub satisfied: bool,
203    /// Ops required by the profile that the engine does not declare.
204    pub missing_ops: BTreeSet<OpKind>,
205    /// Exprs required by the profile that the engine does not declare.
206    pub missing_exprs: BTreeSet<ExprKind>,
207}