1use std::str::FromStr;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub enum LogicalPlanKind {
21 CreateSchema,
22 AlterSchema,
23 CreateIndex,
24 CreateConstraint,
25 CreateNode,
26 MergeNode,
27 MergeRelationship,
28 SetProperty,
29 Delete,
30 NodeScan,
31 NodeSeek,
32 RelationshipExpand,
33 OptionalRelationshipExpand,
34 ShortestPath,
35 Filter,
36 Project,
37 Aggregate,
38 Distinct,
39 Sort,
40 Limit,
41 ProcedureCall,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
45pub enum LogicalPlanClass {
46 Schema,
47 Mutation,
48 Access,
49 Traversal,
50 Relational,
51 Procedure,
52}
53
54pub trait LogicalPlanNode {
55 fn logical_kind(&self) -> LogicalPlanKind;
56
57 fn logical_class(&self) -> LogicalPlanClass {
58 self.logical_kind().class()
59 }
60}
61
62impl LogicalPlanKind {
63 pub fn all() -> &'static [Self] {
64 const ALL: &[LogicalPlanKind] = &[
65 LogicalPlanKind::CreateSchema,
66 LogicalPlanKind::AlterSchema,
67 LogicalPlanKind::CreateIndex,
68 LogicalPlanKind::CreateConstraint,
69 LogicalPlanKind::CreateNode,
70 LogicalPlanKind::MergeNode,
71 LogicalPlanKind::MergeRelationship,
72 LogicalPlanKind::SetProperty,
73 LogicalPlanKind::Delete,
74 LogicalPlanKind::NodeScan,
75 LogicalPlanKind::NodeSeek,
76 LogicalPlanKind::RelationshipExpand,
77 LogicalPlanKind::OptionalRelationshipExpand,
78 LogicalPlanKind::ShortestPath,
79 LogicalPlanKind::Filter,
80 LogicalPlanKind::Project,
81 LogicalPlanKind::Aggregate,
82 LogicalPlanKind::Distinct,
83 LogicalPlanKind::Sort,
84 LogicalPlanKind::Limit,
85 LogicalPlanKind::ProcedureCall,
86 ];
87 ALL
88 }
89
90 pub fn as_str(self) -> &'static str {
91 match self {
92 LogicalPlanKind::CreateSchema => "CreateSchema",
93 LogicalPlanKind::AlterSchema => "AlterSchema",
94 LogicalPlanKind::CreateIndex => "CreateIndex",
95 LogicalPlanKind::CreateConstraint => "CreateConstraint",
96 LogicalPlanKind::CreateNode => "CreateNode",
97 LogicalPlanKind::MergeNode => "MergeNode",
98 LogicalPlanKind::MergeRelationship => "MergeRelationship",
99 LogicalPlanKind::SetProperty => "SetProperty",
100 LogicalPlanKind::Delete => "Delete",
101 LogicalPlanKind::NodeScan => "NodeScan",
102 LogicalPlanKind::NodeSeek => "NodeSeek",
103 LogicalPlanKind::RelationshipExpand => "RelationshipExpand",
104 LogicalPlanKind::OptionalRelationshipExpand => "OptionalRelationshipExpand",
105 LogicalPlanKind::ShortestPath => "ShortestPath",
106 LogicalPlanKind::Filter => "Filter",
107 LogicalPlanKind::Project => "Project",
108 LogicalPlanKind::Aggregate => "Aggregate",
109 LogicalPlanKind::Distinct => "Distinct",
110 LogicalPlanKind::Sort => "Sort",
111 LogicalPlanKind::Limit => "Limit",
112 LogicalPlanKind::ProcedureCall => "ProcedureCall",
113 }
114 }
115
116 pub fn class(self) -> LogicalPlanClass {
117 match self {
118 LogicalPlanKind::CreateSchema
119 | LogicalPlanKind::AlterSchema
120 | LogicalPlanKind::CreateIndex
121 | LogicalPlanKind::CreateConstraint => LogicalPlanClass::Schema,
122 LogicalPlanKind::CreateNode
123 | LogicalPlanKind::MergeNode
124 | LogicalPlanKind::MergeRelationship
125 | LogicalPlanKind::SetProperty
126 | LogicalPlanKind::Delete => LogicalPlanClass::Mutation,
127 LogicalPlanKind::NodeScan | LogicalPlanKind::NodeSeek => LogicalPlanClass::Access,
128 LogicalPlanKind::RelationshipExpand
129 | LogicalPlanKind::OptionalRelationshipExpand
130 | LogicalPlanKind::ShortestPath => LogicalPlanClass::Traversal,
131 LogicalPlanKind::Filter
132 | LogicalPlanKind::Project
133 | LogicalPlanKind::Aggregate
134 | LogicalPlanKind::Distinct
135 | LogicalPlanKind::Sort
136 | LogicalPlanKind::Limit => LogicalPlanClass::Relational,
137 LogicalPlanKind::ProcedureCall => LogicalPlanClass::Procedure,
138 }
139 }
140}
141
142impl LogicalPlanClass {
143 pub fn all() -> &'static [Self] {
144 const ALL: &[LogicalPlanClass] = &[
145 LogicalPlanClass::Schema,
146 LogicalPlanClass::Mutation,
147 LogicalPlanClass::Access,
148 LogicalPlanClass::Traversal,
149 LogicalPlanClass::Relational,
150 LogicalPlanClass::Procedure,
151 ];
152 ALL
153 }
154
155 pub fn as_str(self) -> &'static str {
156 match self {
157 LogicalPlanClass::Schema => "schema",
158 LogicalPlanClass::Mutation => "mutation",
159 LogicalPlanClass::Access => "access",
160 LogicalPlanClass::Traversal => "traversal",
161 LogicalPlanClass::Relational => "relational",
162 LogicalPlanClass::Procedure => "procedure",
163 }
164 }
165}
166
167impl FromStr for LogicalPlanKind {
168 type Err = &'static str;
169
170 fn from_str(value: &str) -> Result<Self, Self::Err> {
171 Self::all()
172 .iter()
173 .copied()
174 .find(|kind| kind.as_str() == value)
175 .ok_or("unknown logical plan kind")
176 }
177}
178
179impl FromStr for LogicalPlanClass {
180 type Err = &'static str;
181
182 fn from_str(value: &str) -> Result<Self, Self::Err> {
183 Self::all()
184 .iter()
185 .copied()
186 .find(|class| class.as_str() == value)
187 .ok_or("unknown logical plan class")
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::{LogicalPlanClass, LogicalPlanKind, LogicalPlanNode};
194
195 #[test]
196 fn logical_plan_kind_exposes_stable_strings_and_classes() {
197 assert_eq!(LogicalPlanKind::NodeSeek.as_str(), "NodeSeek");
198 assert_eq!(LogicalPlanKind::NodeSeek.class(), LogicalPlanClass::Access);
199 assert_eq!(
200 LogicalPlanKind::RelationshipExpand.class(),
201 LogicalPlanClass::Traversal
202 );
203 assert_eq!(
204 LogicalPlanKind::Project.class(),
205 LogicalPlanClass::Relational
206 );
207 }
208
209 #[test]
210 fn logical_plan_kind_strings_round_trip_for_diagnostics() {
211 for kind in LogicalPlanKind::all() {
212 assert_eq!(kind.as_str().parse::<LogicalPlanKind>(), Ok(*kind));
213 }
214 assert!("UnknownLogical".parse::<LogicalPlanKind>().is_err());
215 }
216
217 #[test]
218 fn logical_plan_class_strings_round_trip_for_diagnostics() {
219 for class in LogicalPlanClass::all() {
220 assert_eq!(class.as_str().parse::<LogicalPlanClass>(), Ok(*class));
221 }
222 assert!("unknown".parse::<LogicalPlanClass>().is_err());
223 }
224
225 struct TestLogicalNode;
226
227 impl LogicalPlanNode for TestLogicalNode {
228 fn logical_kind(&self) -> LogicalPlanKind {
229 LogicalPlanKind::Filter
230 }
231 }
232
233 #[test]
234 fn logical_plan_node_derives_class_from_kind() {
235 let node = TestLogicalNode;
236 assert_eq!(node.logical_class(), LogicalPlanClass::Relational);
237 }
238}