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