1use crate::code_block::{Arg, CodeBlock, FormatPart};
9use crate::type_name::TypeName;
10
11#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
16#[non_exhaustive]
17pub enum CodeNode {
18 Literal(String),
20 TypeRef(TypeName),
22 NameRef(String),
24 StringLit(String),
27 InlineLiteral(String),
29 Nested(CodeBlock),
31 Comment(String),
34 SoftBreak,
37 Indent,
39 Dedent,
41 StatementBegin,
43 StatementEnd,
45 Newline,
47 BlockOpen,
49 BlockOpenOverride(String),
51 BlockClose,
53 BlockCloseTransition,
56 Sequence(Vec<CodeNode>),
58}
59
60pub(crate) fn parts_args_to_nodes(parts: &[FormatPart], args: &[Arg]) -> Vec<CodeNode> {
65 let mut nodes = Vec::with_capacity(parts.len());
66 let mut arg_index = 0;
67
68 for part in parts {
69 let node = match part {
70 FormatPart::Literal(text) => CodeNode::Literal(text.clone()),
71 FormatPart::Type => {
72 let arg = &args[arg_index];
73 arg_index += 1;
74 match arg {
75 Arg::TypeName(tn) => CodeNode::TypeRef(tn.clone()),
76 _ => CodeNode::Literal(String::new()),
77 }
78 }
79 FormatPart::Name => {
80 let arg = &args[arg_index];
81 arg_index += 1;
82 match arg {
83 Arg::Name(n) => CodeNode::NameRef(n.clone()),
84 _ => CodeNode::Literal(String::new()),
85 }
86 }
87 FormatPart::StringLit => {
88 let arg = &args[arg_index];
89 arg_index += 1;
90 match arg {
91 Arg::StringLit(s) => CodeNode::StringLit(s.clone()),
92 _ => CodeNode::Literal(String::new()),
93 }
94 }
95 FormatPart::Literal_ => {
96 let arg = &args[arg_index];
97 arg_index += 1;
98 match arg {
99 Arg::Literal(s) => CodeNode::InlineLiteral(s.clone()),
100 Arg::Code(block) => CodeNode::Nested(block.clone()),
101 _ => CodeNode::Literal(String::new()),
102 }
103 }
104 FormatPart::Wrap => CodeNode::SoftBreak,
105 FormatPart::Indent => CodeNode::Indent,
106 FormatPart::Dedent => CodeNode::Dedent,
107 FormatPart::StatementBegin => CodeNode::StatementBegin,
108 FormatPart::StatementEnd => CodeNode::StatementEnd,
109 FormatPart::Newline => CodeNode::Newline,
110 FormatPart::BlockOpen => CodeNode::BlockOpen,
111 FormatPart::BlockOpenOverride(s) => CodeNode::BlockOpenOverride(s.clone()),
112 FormatPart::BlockClose => CodeNode::BlockClose,
113 FormatPart::BlockCloseTransition => CodeNode::BlockCloseTransition,
114 };
115 nodes.push(node);
116 }
117
118 nodes
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use crate::code_block::CodeBlock;
125 use crate::type_name::TypeName;
126
127 #[test]
128 fn test_literal_conversion() {
129 let parts = vec![FormatPart::Literal("hello".to_string())];
130 let args = vec![];
131 let nodes = parts_args_to_nodes(&parts, &args);
132 assert_eq!(nodes.len(), 1);
133 assert!(matches!(&nodes[0], CodeNode::Literal(s) if s == "hello"));
134 }
135
136 #[test]
137 fn test_type_ref_conversion() {
138 let tn = TypeName::primitive("string");
139 let parts = vec![FormatPart::Literal("x: ".to_string()), FormatPart::Type];
140 let args = vec![Arg::TypeName(tn)];
141 let nodes = parts_args_to_nodes(&parts, &args);
142 assert_eq!(nodes.len(), 2);
143 assert!(matches!(&nodes[0], CodeNode::Literal(s) if s == "x: "));
144 assert!(matches!(&nodes[1], CodeNode::TypeRef(_)));
145 }
146
147 #[test]
148 fn test_nested_block_conversion() {
149 let inner = CodeBlock::of("inner()", ()).unwrap();
150 let parts = vec![FormatPart::Literal_];
151 let args = vec![Arg::Code(inner)];
152 let nodes = parts_args_to_nodes(&parts, &args);
153 assert_eq!(nodes.len(), 1);
154 assert!(matches!(&nodes[0], CodeNode::Nested(_)));
155 }
156
157 #[test]
158 fn test_structural_nodes() {
159 let parts = vec![
160 FormatPart::Indent,
161 FormatPart::StatementBegin,
162 FormatPart::Literal("x".to_string()),
163 FormatPart::StatementEnd,
164 FormatPart::Newline,
165 FormatPart::Dedent,
166 ];
167 let nodes = parts_args_to_nodes(&parts, &[]);
168 assert_eq!(nodes.len(), 6);
169 assert!(matches!(nodes[0], CodeNode::Indent));
170 assert!(matches!(nodes[1], CodeNode::StatementBegin));
171 assert!(matches!(nodes[3], CodeNode::StatementEnd));
172 assert!(matches!(nodes[4], CodeNode::Newline));
173 assert!(matches!(nodes[5], CodeNode::Dedent));
174 }
175
176 #[test]
177 fn test_soft_break_conversion() {
178 let parts = vec![
179 FormatPart::Literal("a".to_string()),
180 FormatPart::Wrap,
181 FormatPart::Literal("b".to_string()),
182 ];
183 let nodes = parts_args_to_nodes(&parts, &[]);
184 assert_eq!(nodes.len(), 3);
185 assert!(matches!(nodes[1], CodeNode::SoftBreak));
186 }
187
188 #[test]
189 fn test_block_open_close_conversion() {
190 let parts = vec![
191 FormatPart::BlockOpen,
192 FormatPart::BlockClose,
193 FormatPart::BlockOpenOverride("where".to_string()),
194 FormatPart::BlockCloseTransition,
195 ];
196 let nodes = parts_args_to_nodes(&parts, &[]);
197 assert_eq!(nodes.len(), 4);
198 assert!(matches!(nodes[0], CodeNode::BlockOpen));
199 assert!(matches!(nodes[1], CodeNode::BlockClose));
200 assert!(matches!(&nodes[2], CodeNode::BlockOpenOverride(s) if s == "where"));
201 assert!(matches!(nodes[3], CodeNode::BlockCloseTransition));
202 }
203
204 #[test]
205 fn test_mixed_args_conversion() {
206 let tn = TypeName::primitive("number");
207 let parts = vec![
208 FormatPart::Literal("let ".to_string()),
209 FormatPart::Name,
210 FormatPart::Literal(": ".to_string()),
211 FormatPart::Type,
212 FormatPart::Literal(" = ".to_string()),
213 FormatPart::StringLit,
214 ];
215 let args = vec![
216 Arg::Name("x".to_string()),
217 Arg::TypeName(tn),
218 Arg::StringLit("hello".to_string()),
219 ];
220 let nodes = parts_args_to_nodes(&parts, &args);
221 assert_eq!(nodes.len(), 6);
222 assert!(matches!(&nodes[1], CodeNode::NameRef(s) if s == "x"));
223 assert!(matches!(&nodes[3], CodeNode::TypeRef(_)));
224 assert!(matches!(&nodes[5], CodeNode::StringLit(s) if s == "hello"));
225 }
226}