use std::path::PathBuf;
use plexus_engine::{
flagship_graph_rag_reference_contract, proof_fixture_graph, IndependentConsumerEngine,
PlanEngine, Value,
};
use plexus_serde::{
current_plan_version, CmpOp, ColDef, ColKind, Expr, LogicalType, Op, Plan, SortDir,
};
use serde::Serialize;
#[derive(Debug, Serialize)]
struct IndependentConsumerProofReport {
proof_id: String,
consumer: String,
implementation: String,
supported_subset: SupportedSubset,
reference_contract: plexus_engine::FlagshipGraphRagReferenceContract,
case_sources: Vec<String>,
total: usize,
passed: usize,
failed: usize,
cases: Vec<IndependentConsumerProofCase>,
}
#[derive(Debug, Serialize)]
struct SupportedSubset {
ops: Vec<&'static str>,
exprs: Vec<&'static str>,
notes: Vec<&'static str>,
}
#[derive(Debug, Serialize)]
struct IndependentConsumerProofCase {
case: String,
passed: bool,
error: Option<String>,
}
fn main() -> Result<(), String> {
let case_paths = proof_case_paths();
let cases = proof_cases();
let mut reports = Vec::with_capacity(cases.len());
let mut passed = 0usize;
for case in cases {
let mut engine = IndependentConsumerEngine::new(proof_fixture_graph());
let outcome = engine
.execute_plan(&case.plan)
.map_err(|err| err.to_string());
let assertion = match outcome {
Ok(result) if result.rows == case.expected_rows => Ok(()),
Ok(result) => Err(format!(
"row mismatch: expected {:?}, got {:?}",
case.expected_rows, result.rows
)),
Err(err) => Err(err),
};
if assertion.is_ok() {
passed += 1;
}
reports.push(IndependentConsumerProofCase {
case: case.name,
passed: assertion.is_ok(),
error: assertion.err(),
});
}
let report = IndependentConsumerProofReport {
proof_id: "plexus-independent-consumer-proof-v1".to_string(),
consumer: "independent-consumer-example".to_string(),
implementation: "independent PlanEngine implementation with a private flat graph store"
.to_string(),
supported_subset: SupportedSubset {
ops: vec![
"ScanNodes",
"Expand",
"OptionalExpand",
"Filter",
"Project",
"Sort",
"Return",
],
exprs: vec![
"ColRef",
"PropAccess",
"IntLiteral",
"FloatLiteral",
"BoolLiteral",
"StringLiteral",
"NullLiteral",
"Cmp",
],
notes: vec![
"This proof intentionally covers a bounded core-read subset only.",
"The example does not reuse the reference engine execution path.",
"The checked-in JSON artifact is produced by running this example.",
],
},
reference_contract: flagship_graph_rag_reference_contract(),
case_sources: case_paths
.iter()
.map(|path| path.display().to_string())
.collect(),
total: reports.len(),
passed,
failed: reports.len().saturating_sub(passed),
cases: reports,
};
println!(
"{}",
serde_json::to_string_pretty(&report).map_err(|err| err.to_string())?
);
Ok(())
}
fn proof_case_paths() -> Vec<PathBuf> {
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src/tests/fixtures/readonly");
vec![
root.join("001_match_return_order_by.case"),
root.join("002_match_expand_filter.case"),
root.join("003_optional_match_null_projection.case"),
]
}
struct ProofCase {
name: String,
plan: Plan,
expected_rows: Vec<Vec<Value>>,
}
fn proof_cases() -> Vec<ProofCase> {
let value_col = || ColDef {
name: "value".to_string(),
kind: ColKind::Value,
logical_type: LogicalType::Unknown,
};
let node_col = |name: &str| ColDef {
name: name.to_string(),
kind: ColKind::Node,
logical_type: LogicalType::Unknown,
};
let rel_col = |name: &str| ColDef {
name: name.to_string(),
kind: ColKind::Rel,
logical_type: LogicalType::Unknown,
};
let version = current_plan_version("independent-consumer-proof");
vec![
ProofCase {
name: "match-return-order-by".to_string(),
plan: Plan {
version: version.clone(),
ops: vec![
Op::ScanNodes {
labels: vec!["Person".to_string()],
schema: vec![node_col("n")],
must_labels: vec!["Person".to_string()],
forbidden_labels: vec![],
est_rows: -1,
selectivity: 1.0,
graph_ref: None,
},
Op::Project {
input: 0,
exprs: vec![Expr::PropAccess {
col: 0,
prop: "name".to_string(),
}],
schema: vec![value_col()],
},
Op::Sort {
input: 1,
keys: vec![0],
dirs: vec![SortDir::Asc],
},
Op::Return { input: 2 },
],
root_op: 3,
},
expected_rows: vec![
vec![Value::String("Alice".to_string())],
vec![Value::String("Bob".to_string())],
],
},
ProofCase {
name: "match-expand-filter".to_string(),
plan: Plan {
version: version.clone(),
ops: vec![
Op::ScanNodes {
labels: vec!["Person".to_string()],
schema: vec![node_col("n")],
must_labels: vec!["Person".to_string()],
forbidden_labels: vec![],
est_rows: -1,
selectivity: 1.0,
graph_ref: None,
},
Op::Expand {
input: 0,
src_col: 0,
types: vec!["KNOWS".to_string()],
dir: plexus_serde::ExpandDir::Out,
schema: vec![node_col("n"), rel_col("r"), node_col("m")],
src_var: "n".to_string(),
rel_var: "r".to_string(),
dst_var: "m".to_string(),
legal_src_labels: vec!["Person".to_string()],
legal_dst_labels: vec!["Person".to_string()],
est_degree: 1.0,
graph_ref: None,
},
Op::Filter {
input: 1,
predicate: Expr::Cmp {
op: CmpOp::Gt,
lhs: Box::new(Expr::PropAccess {
col: 2,
prop: "age".to_string(),
}),
rhs: Box::new(Expr::IntLiteral(30)),
},
},
Op::Project {
input: 2,
exprs: vec![Expr::PropAccess {
col: 0,
prop: "name".to_string(),
}],
schema: vec![value_col()],
},
Op::Sort {
input: 3,
keys: vec![0],
dirs: vec![SortDir::Asc],
},
Op::Return { input: 4 },
],
root_op: 5,
},
expected_rows: vec![vec![Value::String("Alice".to_string())]],
},
ProofCase {
name: "optional-match-null-projection".to_string(),
plan: Plan {
version,
ops: vec![
Op::ScanNodes {
labels: vec!["Person".to_string()],
schema: vec![node_col("n")],
must_labels: vec!["Person".to_string()],
forbidden_labels: vec![],
est_rows: -1,
selectivity: 1.0,
graph_ref: None,
},
Op::OptionalExpand {
input: 0,
src_col: 0,
types: vec!["WORKS_AT".to_string()],
dir: plexus_serde::ExpandDir::Out,
schema: vec![node_col("n"), rel_col("r"), node_col("c")],
src_var: "n".to_string(),
rel_var: "r".to_string(),
dst_var: "c".to_string(),
legal_src_labels: vec!["Person".to_string()],
legal_dst_labels: vec!["Company".to_string()],
graph_ref: None,
},
Op::Project {
input: 1,
exprs: vec![Expr::PropAccess {
col: 2,
prop: "name".to_string(),
}],
schema: vec![value_col()],
},
Op::Sort {
input: 2,
keys: vec![0],
dirs: vec![SortDir::Asc],
},
Op::Return { input: 3 },
],
root_op: 4,
},
expected_rows: vec![vec![Value::Null], vec![Value::String("Acme".to_string())]],
},
]
}