Skip to main content

nodedb_physical/
visitor.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Executor parity contract for `PhysicalPlan`.
4//!
5//! Every NodeDB deployment that executes physical plans implements
6//! [`PhysicalTaskVisitor`] — Origin's Data Plane handlers and Lite's
7//! embedded executor both. The trait has no default methods, so adding
8//! a new [`PhysicalPlan`] variant becomes a hard compile error on every
9//! implementation until it is handled.
10//!
11//! Method-level granularity is one method per top-level `PhysicalPlan`
12//! variant (engine family). Each method receives the variant's inner op
13//! enum; the implementer pattern-matches as it sees fit.
14
15use crate::physical_plan::{
16    ArrayOp, ClusterArrayOp, ClusterEventOp, ColumnarOp, CrdtOp, DocumentOp, GraphOp, KvOp, MetaOp,
17    PhysicalPlan, QueryOp, SpatialOp, TextOp, TimeseriesOp, VectorOp,
18};
19
20/// Per-deployment executor for `PhysicalPlan`.
21///
22/// Implementations decide their own `Output` and `Error`. Origin's Data
23/// Plane handlers may return a row stream; Lite's executor may return a
24/// boxed future to a `QueryResult`. The trait stays sync — async backends
25/// box their futures and resolve at the call site, mirroring the pattern
26/// used by `LiteVisitor` for `PlanVisitor`.
27pub trait PhysicalTaskVisitor {
28    type Output;
29    type Error;
30
31    fn vector(&mut self, op: &VectorOp) -> Result<Self::Output, Self::Error>;
32    fn graph(&mut self, op: &GraphOp) -> Result<Self::Output, Self::Error>;
33    fn document(&mut self, op: &DocumentOp) -> Result<Self::Output, Self::Error>;
34    fn kv(&mut self, op: &KvOp) -> Result<Self::Output, Self::Error>;
35    fn text(&mut self, op: &TextOp) -> Result<Self::Output, Self::Error>;
36    fn columnar(&mut self, op: &ColumnarOp) -> Result<Self::Output, Self::Error>;
37    fn timeseries(&mut self, op: &TimeseriesOp) -> Result<Self::Output, Self::Error>;
38    fn spatial(&mut self, op: &SpatialOp) -> Result<Self::Output, Self::Error>;
39    fn crdt(&mut self, op: &CrdtOp) -> Result<Self::Output, Self::Error>;
40    fn query(&mut self, op: &QueryOp) -> Result<Self::Output, Self::Error>;
41    fn meta(&mut self, op: &MetaOp) -> Result<Self::Output, Self::Error>;
42    fn array(&mut self, op: &ArrayOp) -> Result<Self::Output, Self::Error>;
43    fn cluster_array(&mut self, op: &ClusterArrayOp) -> Result<Self::Output, Self::Error>;
44    fn cluster_event(&mut self, op: &ClusterEventOp) -> Result<Self::Output, Self::Error>;
45}
46
47/// Dispatch `plan` to the matching method on `visitor`.
48/// Adding a [`PhysicalPlan`] variant without a corresponding arm is a
49/// compile error.
50pub fn dispatch<V: PhysicalTaskVisitor>(
51    visitor: &mut V,
52    plan: &PhysicalPlan,
53) -> Result<V::Output, V::Error> {
54    match plan {
55        PhysicalPlan::Vector(op) => visitor.vector(op),
56        PhysicalPlan::Graph(op) => visitor.graph(op),
57        PhysicalPlan::Document(op) => visitor.document(op),
58        PhysicalPlan::Kv(op) => visitor.kv(op),
59        PhysicalPlan::Text(op) => visitor.text(op),
60        PhysicalPlan::Columnar(op) => visitor.columnar(op),
61        PhysicalPlan::Timeseries(op) => visitor.timeseries(op),
62        PhysicalPlan::Spatial(op) => visitor.spatial(op),
63        PhysicalPlan::Crdt(op) => visitor.crdt(op),
64        PhysicalPlan::Query(op) => visitor.query(op),
65        PhysicalPlan::Meta(op) => visitor.meta(op),
66        PhysicalPlan::Array(op) => visitor.array(op),
67        PhysicalPlan::ClusterArray(op) => visitor.cluster_array(op),
68        PhysicalPlan::ClusterEvent(op) => visitor.cluster_event(op),
69    }
70}