fluidattacks_blends_domain/syntax/cfg/
generate.rs1use alloc::borrow::ToOwned;
4use alloc::string::String;
5
6use crate::syntax::cfg::dispatchers;
7use crate::syntax::SyntaxGraph;
8use crate::{CodeGraph, NodeId};
9
10#[derive(Clone, PartialEq, Eq, Debug)]
11pub enum SyntaxCfgError {
12 MissingCfgBuilder { node_type: String },
13 MissingSyntaxNode { n_id: NodeId },
14 UnexpectedSyntaxShape { n_id: NodeId },
15}
16
17impl core::fmt::Display for SyntaxCfgError {
18 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
19 match self {
20 Self::MissingCfgBuilder { node_type } => {
21 write!(formatter, "missing cfg builder for {node_type}")
22 }
23 Self::MissingSyntaxNode { n_id } => {
24 write!(
25 formatter,
26 "the cfg walk reached id {} absent from the syntax graph",
27 n_id.0
28 )
29 }
30 Self::UnexpectedSyntaxShape { n_id } => {
31 write!(
32 formatter,
33 "the cfg walk found an unexpected syntax shape at id {}",
34 n_id.0
35 )
36 }
37 }
38 }
39}
40
41pub type CfgBuilder = fn(
42 args: &mut SyntaxCfgArgs<'_>,
43 n_id: NodeId,
44 nxt_id: Option<NodeId>,
45) -> Result<NodeId, SyntaxCfgError>;
46
47pub struct SyntaxCfgArgs<'a> {
48 pub graph: &'a mut SyntaxGraph,
49 pub is_multifile: bool,
50}
51
52impl SyntaxCfgArgs<'_> {
53 pub fn generic(
54 &mut self,
55 n_id: NodeId,
56 nxt_id: Option<NodeId>,
57 ) -> Result<NodeId, SyntaxCfgError> {
58 let node_type = self
59 .graph
60 .label_type(n_id)
61 .ok_or(SyntaxCfgError::MissingSyntaxNode { n_id })?;
62
63 let builder = dispatchers::dispatcher(node_type).ok_or_else(|| {
64 SyntaxCfgError::MissingCfgBuilder {
65 node_type: node_type.to_owned(),
66 }
67 })?;
68
69 builder(self, n_id, nxt_id)
70 }
71}
72
73pub fn add_syntax_cfg(graph: &mut SyntaxGraph, is_multifile: bool) -> Result<(), SyntaxCfgError> {
74 let mut args = SyntaxCfgArgs {
75 graph,
76 is_multifile,
77 };
78 args.generic(NodeId(1), None)?;
79 Ok(())
80}
81
82#[cfg(test)]
83mod tests {
84 use super::{add_syntax_cfg, SyntaxCfgError};
85 use crate::syntax::{SyntaxGraph, SyntaxNode};
86 use crate::{Cfg, NodeId};
87 use alloc::borrow::ToOwned;
88 use alloc::string::ToString;
89
90 #[test]
91 fn unmapped_node_type_fails_with_the_missing_builder() {
92 let mut graph = SyntaxGraph::new();
93 graph.add_node(
94 NodeId(1),
95 SyntaxNode::Metadata {
96 path: "test.yaml".to_owned(),
97 structure: alloc::collections::BTreeMap::new(),
98 instances: alloc::collections::BTreeMap::new(),
99 imports: alloc::vec::Vec::new(),
100 package: None,
101 },
102 );
103
104 let result = add_syntax_cfg(&mut graph, false);
105
106 assert_eq!(
107 result,
108 Err(SyntaxCfgError::MissingCfgBuilder {
109 node_type: "Metadata".to_owned()
110 })
111 );
112 }
113
114 #[test]
115 fn every_error_renders_its_failure_mode() {
116 assert_eq!(
117 SyntaxCfgError::MissingCfgBuilder {
118 node_type: "Metadata".to_owned()
119 }
120 .to_string(),
121 "missing cfg builder for Metadata"
122 );
123 assert_eq!(
124 SyntaxCfgError::MissingSyntaxNode { n_id: NodeId(7) }.to_string(),
125 "the cfg walk reached id 7 absent from the syntax graph"
126 );
127 assert_eq!(
128 SyntaxCfgError::UnexpectedSyntaxShape { n_id: NodeId(9) }.to_string(),
129 "the cfg walk found an unexpected syntax shape at id 9"
130 );
131 }
132
133 #[test]
134 fn missing_root_node_fails() {
135 let mut graph = SyntaxGraph::new();
136
137 let result = add_syntax_cfg(&mut graph, false);
138
139 assert_eq!(
140 result,
141 Err(SyntaxCfgError::MissingSyntaxNode { n_id: NodeId(1) })
142 );
143 }
144
145 #[test]
146 fn failed_walk_keeps_the_partial_overlay() {
147 let mut graph = SyntaxGraph::new();
148 graph.add_node(NodeId(1), SyntaxNode::File);
149 graph.add_node(
150 NodeId(2),
151 SyntaxNode::Literal {
152 value: "1".to_owned(),
153 value_type: "number".to_owned(),
154 },
155 );
156 graph.add_node(
157 NodeId(3),
158 SyntaxNode::Metadata {
159 path: "test.yaml".to_owned(),
160 structure: alloc::collections::BTreeMap::new(),
161 instances: alloc::collections::BTreeMap::new(),
162 imports: alloc::vec::Vec::new(),
163 package: None,
164 },
165 );
166 graph.add_ast_edge(NodeId(1), NodeId(2));
167 graph.add_ast_edge(NodeId(1), NodeId(3));
168
169 let result = add_syntax_cfg(&mut graph, false);
170
171 assert!(matches!(
172 result,
173 Err(SyntaxCfgError::MissingCfgBuilder { .. })
174 ));
175 assert_eq!(
176 graph
177 .edges
178 .get(&NodeId(1))
179 .and_then(|adjacent| adjacent.get(&NodeId(2)))
180 .and_then(|edge| edge.cfg),
181 Some(Cfg)
182 );
183 }
184}