1use std::cmp::Ordering;
2use std::collections::{BTreeMap, HashMap, HashSet};
3
4pub use capabilities::{
5 check_version_compat, op_ordering_contract, required_capabilities,
6 validate_plan_against_capabilities, CapabilityError, EngineCapabilities,
7 EngineCapabilityDocument, ExprKind, OpKind, OpOrderingContract, OpOrderingDocument, PlanSemver,
8 RequiredCapabilities, VersionRange, ALL_EXPR_KINDS, ALL_OP_KINDS,
9};
10pub use embedded_profile::{
11 assess_embedded_graph_rag_capabilities, assess_embedded_graph_rag_plan,
12 validate_embedded_graph_rag_plan, EmbeddedGraphRagAssessment, EmbeddedGraphRagCapabilityGap,
13 EmbeddedGraphRagProfileError, EMBEDDED_GRAPH_RAG_PROFILE, EMBEDDED_PHASE_1_RELEASE_CANDIDATE,
14 PLEXUS_CAPABILITY_REJECTION_SCENARIO,
15};
16pub use independent_consumer::{proof_fixture_graph, IndependentConsumerEngine};
17use plexus_serde::{
18 deserialize_plan, validate_plan_structure, AggFn, CmpOp, ExpandDir, Expr, Op, Plan, SortDir,
19};
20pub use reference_topology::{
21 build_compiled_plan_cache_descriptor, flagship_graph_rag_reference_contract,
22 CompiledPlanCacheContract, CompiledPlanCacheDescriptor, CompiledPlanCacheRequest,
23 FlagshipGraphRagReferenceContract, COMPILED_PLAN_CACHE_NAMESPACE, COMPILED_PLAN_CACHE_SCHEMA,
24 FLAGSHIP_COMPATIBILITY_PROFILE, FLAGSHIP_GRAPH_RAG_REFERENCE_TOPOLOGY,
25 FLAGSHIP_PHASE_3_RELEASE_CANDIDATE, RHODIUM_COMPILED_PLAN_CACHE_ROLE,
26};
27
28#[derive(Debug, Clone, PartialEq)]
29pub enum Value {
30 Null,
31 Bool(bool),
32 Int(i64),
33 Float(f64),
34 String(String),
35 NodeRef(u64),
36 RelRef(u64),
37 List(Vec<Value>),
38 Map(BTreeMap<String, Value>),
39}
40
41pub type Row = Vec<Value>;
42
43type RowSet = Vec<Row>;
44
45#[derive(Debug, Clone, PartialEq)]
46pub struct QueryResult {
47 pub rows: Vec<Row>,
48 pub continuation: Option<Vec<u8>>,
53}
54
55impl QueryResult {
56 pub fn empty() -> Self {
57 Self {
58 rows: Vec::new(),
59 continuation: None,
60 }
61 }
62}
63
64#[derive(Debug, Clone, PartialEq)]
65pub struct Node {
66 pub id: u64,
67 pub labels: HashSet<String>,
68 pub props: HashMap<String, Value>,
69}
70
71#[derive(Debug, Clone, PartialEq)]
72pub struct Relationship {
73 pub id: u64,
74 pub src: u64,
75 pub dst: u64,
76 pub typ: String,
77 pub props: HashMap<String, Value>,
78}
79
80#[derive(Debug, Clone, PartialEq, Default)]
87pub struct Graph {
88 pub nodes: Vec<Node>,
89 pub rels: Vec<Relationship>,
90}
91
92impl Graph {
93 pub fn node_by_id(&self, id: u64) -> Option<&Node> {
95 self.nodes.iter().find(|n| n.id == id)
96 }
97
98 pub fn node_by_id_mut(&mut self, id: u64) -> Option<&mut Node> {
100 self.nodes.iter_mut().find(|n| n.id == id)
101 }
102
103 pub fn rel_by_id(&self, id: u64) -> Option<&Relationship> {
105 self.rels.iter().find(|r| r.id == id)
106 }
107
108 pub fn rel_by_id_mut(&mut self, id: u64) -> Option<&mut Relationship> {
110 self.rels.iter_mut().find(|r| r.id == id)
111 }
112}
113
114#[derive(Debug, thiserror::Error)]
115pub enum ExecutionError {
116 #[error("invalid op reference index {0}")]
117 InvalidOpRef(u32),
118 #[error("missing op output at index {0}")]
119 MissingOpOutput(u32),
120 #[error("invalid root op index {0}")]
121 InvalidRootOp(u32),
122 #[error("row column {idx} out of bounds (len={len})")]
123 ColumnOutOfBounds { idx: usize, len: usize },
124 #[error("expected a boolean value")]
125 ExpectedBool,
126 #[error("expected a numeric value")]
127 ExpectedNumeric,
128 #[error("expected aggregate expression")]
129 ExpectedAggregateExpr,
130 #[error("sort keys/dirs length mismatch ({keys} vs {dirs})")]
131 SortArityMismatch { keys: usize, dirs: usize },
132 #[error("unknown node id {0}")]
133 UnknownNode(u64),
134 #[error("unknown relationship id {0}")]
135 UnknownRel(u64),
136 #[error("expected node reference in column {idx}")]
137 ExpectedNodeRef { idx: usize },
138 #[error("expected relationship reference in column {idx}")]
139 ExpectedRelRef { idx: usize },
140 #[error("expected node or relationship reference in column {idx}")]
141 ExpectedEntityRef { idx: usize },
142 #[error("cannot delete node {0} without detach while relationships exist")]
143 DeleteRequiresDetach(u64),
144 #[error("expected map or null for properties payload")]
145 ExpectedMapPayload,
146 #[error("unsupported op in reference engine: {0}")]
147 UnsupportedOp(&'static str),
148 #[error("unsupported expression in reference engine: {0}")]
149 UnsupportedExpr(&'static str),
150 #[error("unbound parameter: {0}")]
151 UnboundParam(String),
152 #[error("plan requires multi-graph support but engine declares supports_multi_graph=false")]
153 MultiGraphUnsupported,
154}
155
156#[derive(Debug, thiserror::Error)]
157pub enum EngineError<E: std::error::Error + 'static> {
158 #[error("invalid serialized Plexus plan: {0}")]
159 Deserialize(#[from] plexus_serde::SerdeError),
160 #[error("invalid plan structure: {0}")]
161 InvalidStructure(plexus_serde::PlanValidationError),
162 #[error("engine execution failed: {0}")]
163 Engine(E),
164}
165
166pub trait PlanEngine {
168 type Error: std::error::Error + Send + Sync + 'static;
169
170 fn execute_plan(&mut self, plan: &Plan) -> Result<QueryResult, Self::Error>;
172}
173
174pub trait MutationEngine {
176 type Error: std::error::Error + Send + Sync + 'static;
177
178 fn create_node(
179 &mut self,
180 labels: &[String],
181 props: HashMap<String, Value>,
182 ) -> Result<u64, Self::Error>;
183 fn create_rel(
184 &mut self,
185 src: u64,
186 dst: u64,
187 rel_type: &str,
188 props: HashMap<String, Value>,
189 ) -> Result<u64, Self::Error>;
190 fn merge_pattern(
191 &mut self,
192 pattern: &Expr,
193 on_create_props: &Expr,
194 on_match_props: &Expr,
195 schema: &[plexus_serde::ColDef],
196 row: &Row,
197 ) -> Result<(), Self::Error>;
198 fn delete_entity(&mut self, target: &Value, detach: bool) -> Result<(), Self::Error>;
199 fn set_property(&mut self, target: &Value, key: &str, value: Value) -> Result<(), Self::Error>;
200 fn remove_property(&mut self, target: &Value, key: &str) -> Result<(), Self::Error>;
201}
202
203pub fn execute_serialized<E: PlanEngine>(
208 engine: &mut E,
209 bytes: &[u8],
210) -> Result<QueryResult, EngineError<E::Error>> {
211 let plan = deserialize_plan(bytes)?;
212 if let Err(errors) = validate_plan_structure(&plan) {
213 return Err(EngineError::InvalidStructure(
214 errors.into_iter().next().unwrap(),
215 ));
216 }
217 engine.execute_plan(&plan).map_err(EngineError::Engine)
218}
219
220#[derive(Debug, Clone, Default)]
230pub struct InMemoryEngine {
231 pub graph: Graph,
232 pub params: HashMap<String, Value>,
233}
234
235impl InMemoryEngine {
236 pub fn new(graph: Graph) -> Self {
237 Self {
238 graph,
239 params: HashMap::new(),
240 }
241 }
242
243 pub fn with_params(graph: Graph, params: HashMap<String, Value>) -> Self {
244 Self { graph, params }
245 }
246
247 pub fn set_param(&mut self, name: impl Into<String>, value: Value) {
248 self.params.insert(name.into(), value);
249 }
250
251 pub fn clear_params(&mut self) {
252 self.params.clear();
253 }
254}
255
256#[derive(Debug, Clone, PartialEq)]
257pub struct VectorCollectionEntry {
258 pub node_id: u64,
259 pub embedding: Vec<f64>,
260}
261
262#[derive(Debug, Clone, Default)]
263pub struct MockVectorEngine {
264 pub base: InMemoryEngine,
265 pub collections: HashMap<String, Vec<VectorCollectionEntry>>,
266}
267
268impl MockVectorEngine {
269 pub fn new(graph: Graph) -> Self {
270 Self {
271 base: InMemoryEngine::new(graph),
272 collections: HashMap::new(),
273 }
274 }
275
276 pub fn with_collections(
277 graph: Graph,
278 collections: HashMap<String, Vec<VectorCollectionEntry>>,
279 ) -> Self {
280 Self {
281 base: InMemoryEngine::new(graph),
282 collections,
283 }
284 }
285
286 pub fn insert_collection(
287 &mut self,
288 name: impl Into<String>,
289 entries: Vec<VectorCollectionEntry>,
290 ) {
291 self.collections.insert(name.into(), entries);
292 }
293
294 pub fn set_param(&mut self, name: impl Into<String>, value: Value) {
295 self.base.set_param(name, value);
296 }
297
298 pub fn clear_params(&mut self) {
299 self.base.clear_params();
300 }
301}
302
303mod capabilities;
304mod embedded_profile;
305mod independent_consumer;
306mod reference_topology;
307mod storage;
308pub use storage::{EntityRef, StorageAdapter};
309mod engine;
310
311#[cfg(test)]
312mod tests;