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