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