1use std::collections::BTreeMap;
2
3use serde::Serialize;
4
5use super::error::RenderError;
6use super::nodes::TextGeneratorNode;
7use super::state::LoopMetadata;
8use super::state::RenderState;
9
10pub enum StructuredNode {
16 Object(BTreeMap<String, StructuredNode>),
18 Array(Vec<StructuredArrayEntry>),
20 Text(Box<dyn TextGeneratorNode>),
22 Number(CopperlaceNumber),
24 Boolean(bool),
26 Null,
28}
29
30impl StructuredNode {
31 pub(crate) fn child(&self, path: &str) -> Option<&StructuredNode> {
32 if path.is_empty() {
33 return Some(self);
34 }
35
36 let mut node = self;
37 for segment in path.split('.') {
38 let StructuredNode::Object(values) = node else {
39 return None;
40 };
41 node = values.get(segment)?;
42 }
43 Some(node)
44 }
45
46 pub(crate) fn value_type(&self) -> &'static str {
47 match self {
48 StructuredNode::Object(_) => "object",
49 StructuredNode::Array(_) => "array",
50 StructuredNode::Text(_) => "string",
51 StructuredNode::Number(_) => "number",
52 StructuredNode::Boolean(_) => "boolean",
53 StructuredNode::Null => "null",
54 }
55 }
56
57 pub(crate) fn generate_text(&self, state: &mut RenderState) -> Result<String, RenderError> {
58 match self {
59 StructuredNode::Text(node) => node.generate_text(state),
60 StructuredNode::Number(value) => Ok(value.to_string()),
61 StructuredNode::Boolean(value) => Ok(value.to_string()),
62 StructuredNode::Null => Ok("null".to_string()),
63 StructuredNode::Object(_) => Err(RenderError::UnsupportedValue("object".to_string())),
64 StructuredNode::Array(_) => Err(RenderError::UnsupportedValue("array".to_string())),
65 }
66 }
67
68 pub(crate) fn generate_value(
69 &self,
70 state: &mut RenderState,
71 ) -> Result<CopperlaceValue, RenderError> {
72 match self {
73 StructuredNode::Object(values) => values
74 .iter()
75 .map(|(key, value)| Ok((key.clone(), value.generate_value(state)?)))
76 .collect::<Result<BTreeMap<_, _>, _>>()
77 .map(CopperlaceValue::Object),
78 StructuredNode::Array(values) => {
79 let mut rendered = Vec::new();
80 for value in values {
81 value.append_values(state, &mut rendered)?;
82 }
83 Ok(CopperlaceValue::Array(rendered))
84 }
85 StructuredNode::Text(node) => node.generate_text(state).map(CopperlaceValue::String),
86 StructuredNode::Number(value) => Ok(CopperlaceValue::Number(*value)),
87 StructuredNode::Boolean(value) => Ok(CopperlaceValue::Boolean(*value)),
88 StructuredNode::Null => Ok(CopperlaceValue::Null),
89 }
90 }
91}
92
93pub struct StructuredArrayEntry {
99 renderer: Box<dyn StructuredArrayEntryRenderer>,
100}
101
102trait StructuredArrayEntryRenderer {
103 fn append_values(
104 &self,
105 state: &mut RenderState,
106 output: &mut Vec<CopperlaceValue>,
107 ) -> Result<(), RenderError>;
108
109 fn value_node(&self) -> Option<&StructuredNode>;
110
111 fn iteration(&self) -> Option<(&str, &str, &StructuredNode)>;
112}
113
114struct FixedStructuredArrayEntry {
115 value: StructuredNode,
116}
117
118struct ForEachStructuredArrayEntry {
119 variable_name: String,
120 source_name: String,
121 template: StructuredNode,
122}
123
124impl StructuredArrayEntry {
125 pub fn from_value(value: StructuredNode) -> Self {
127 StructuredArrayEntry {
128 renderer: Box::new(FixedStructuredArrayEntry { value }),
129 }
130 }
131
132 pub(crate) fn for_each(
133 variable_name: String,
134 source_name: String,
135 template: StructuredNode,
136 ) -> Self {
137 StructuredArrayEntry {
138 renderer: Box::new(ForEachStructuredArrayEntry {
139 variable_name,
140 source_name,
141 template,
142 }),
143 }
144 }
145
146 pub fn value_node(&self) -> Option<&StructuredNode> {
148 self.renderer.value_node()
149 }
150
151 pub fn iteration(&self) -> Option<(&str, &str, &StructuredNode)> {
153 self.renderer.iteration()
154 }
155
156 pub(crate) fn append_values(
157 &self,
158 state: &mut RenderState,
159 output: &mut Vec<CopperlaceValue>,
160 ) -> Result<(), RenderError> {
161 self.renderer.append_values(state, output)
162 }
163}
164
165impl StructuredArrayEntryRenderer for FixedStructuredArrayEntry {
166 fn append_values(
167 &self,
168 state: &mut RenderState,
169 output: &mut Vec<CopperlaceValue>,
170 ) -> Result<(), RenderError> {
171 output.push(self.value.generate_value(state)?);
172 Ok(())
173 }
174
175 fn value_node(&self) -> Option<&StructuredNode> {
176 Some(&self.value)
177 }
178
179 fn iteration(&self) -> Option<(&str, &str, &StructuredNode)> {
180 None
181 }
182}
183
184impl StructuredArrayEntryRenderer for ForEachStructuredArrayEntry {
185 fn append_values(
186 &self,
187 state: &mut RenderState,
188 output: &mut Vec<CopperlaceValue>,
189 ) -> Result<(), RenderError> {
190 let elements = state.iterable_elements(&self.source_name)?;
191 let length = elements.len();
192 for (index, element) in elements.into_iter().enumerate() {
193 state.push_iteration_scope(
194 &self.variable_name,
195 element,
196 LoopMetadata { index, length },
197 );
198 let rendered = self.template.generate_value(state);
199 state.pop_iteration_scope();
200 output.push(rendered?);
201 }
202 Ok(())
203 }
204
205 fn value_node(&self) -> Option<&StructuredNode> {
206 None
207 }
208
209 fn iteration(&self) -> Option<(&str, &str, &StructuredNode)> {
210 Some((&self.variable_name, &self.source_name, &self.template))
211 }
212}
213
214impl From<StructuredNode> for StructuredArrayEntry {
215 fn from(value: StructuredNode) -> Self {
216 StructuredArrayEntry::from_value(value)
217 }
218}
219
220#[derive(Debug, Clone, PartialEq)]
222pub enum CopperlaceValue {
223 Object(BTreeMap<String, CopperlaceValue>),
225 Array(Vec<CopperlaceValue>),
227 String(String),
229 Number(CopperlaceNumber),
231 Boolean(bool),
233 Null,
235}
236
237impl CopperlaceValue {
238 pub(crate) fn child(&self, path: &str) -> Option<&CopperlaceValue> {
239 if path.is_empty() {
240 return Some(self);
241 }
242
243 let mut value = self;
244 for segment in path.split('.') {
245 let CopperlaceValue::Object(values) = value else {
246 return None;
247 };
248 value = values.get(segment)?;
249 }
250 Some(value)
251 }
252
253 pub(crate) fn value_type(&self) -> &'static str {
254 match self {
255 CopperlaceValue::Object(_) => "object",
256 CopperlaceValue::Array(_) => "array",
257 CopperlaceValue::String(_) => "string",
258 CopperlaceValue::Number(_) => "number",
259 CopperlaceValue::Boolean(_) => "boolean",
260 CopperlaceValue::Null => "null",
261 }
262 }
263
264 pub(crate) fn to_rendered_text(&self) -> Result<String, RenderError> {
265 match self {
266 CopperlaceValue::String(value) => Ok(value.clone()),
267 CopperlaceValue::Number(value) => Ok(value.to_string()),
268 CopperlaceValue::Boolean(value) => Ok(value.to_string()),
269 CopperlaceValue::Null => Ok("null".to_string()),
270 CopperlaceValue::Object(_) => Err(RenderError::UnsupportedValue("object".to_string())),
271 CopperlaceValue::Array(_) => Err(RenderError::UnsupportedValue("array".to_string())),
272 }
273 }
274
275 pub fn into_json_value(self) -> serde_json::Value {
277 match self {
278 CopperlaceValue::Object(values) => serde_json::Value::Object(
279 values
280 .into_iter()
281 .map(|(key, value)| (key, value.into_json_value()))
282 .collect(),
283 ),
284 CopperlaceValue::Array(values) => serde_json::Value::Array(
285 values
286 .into_iter()
287 .map(CopperlaceValue::into_json_value)
288 .collect(),
289 ),
290 CopperlaceValue::String(value) => serde_json::Value::String(value),
291 CopperlaceValue::Number(value) => value.into_json_number(),
292 CopperlaceValue::Boolean(value) => serde_json::Value::Bool(value),
293 CopperlaceValue::Null => serde_json::Value::Null,
294 }
295 }
296
297 pub fn to_json_value(&self) -> serde_json::Value {
299 self.clone().into_json_value()
300 }
301
302 pub fn to_compact_json(&self) -> Result<String, RenderError> {
304 serde_json::to_string(&self.to_json_value())
305 .map_err(|error| RenderError::JsonSerialization(error.to_string()))
306 }
307
308 pub fn to_formatted_json(&self) -> Result<String, RenderError> {
310 let mut output = Vec::new();
311 let formatter = serde_json::ser::PrettyFormatter::with_indent(b"\t");
312 let mut serializer = serde_json::Serializer::with_formatter(&mut output, formatter);
313 self.to_json_value()
314 .serialize(&mut serializer)
315 .map_err(|error| RenderError::JsonSerialization(error.to_string()))?;
316 String::from_utf8(output).map_err(|error| RenderError::JsonSerialization(error.to_string()))
317 }
318}
319
320#[derive(Debug, Clone, Copy, PartialEq)]
322pub enum CopperlaceNumber {
323 Integer(i64),
325 Unsigned(u64),
327 Float(f64),
329}
330
331impl std::fmt::Display for CopperlaceNumber {
332 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
333 match self {
334 CopperlaceNumber::Integer(value) => value.fmt(formatter),
335 CopperlaceNumber::Unsigned(value) => value.fmt(formatter),
336 CopperlaceNumber::Float(value) => {
337 let Some(number) = serde_json::Number::from_f64(*value) else {
338 return Err(std::fmt::Error);
339 };
340 number.fmt(formatter)
341 }
342 }
343 }
344}
345
346impl CopperlaceNumber {
347 pub(crate) fn from_json_number(number: serde_json::Number) -> Result<Self, RenderError> {
348 if let Some(value) = number.as_i64() {
349 return Ok(CopperlaceNumber::Integer(value));
350 }
351 if let Some(value) = number.as_u64() {
352 return Ok(CopperlaceNumber::Unsigned(value));
353 }
354 let Some(value) = number.as_f64() else {
355 return Err(RenderError::UnsupportedValue(
356 "number must be representable as i64, u64, or f64".to_string(),
357 ));
358 };
359 if !value.is_finite() {
360 return Err(RenderError::UnsupportedValue(
361 "number must be finite".to_string(),
362 ));
363 }
364 Ok(CopperlaceNumber::Float(value))
365 }
366
367 fn into_json_number(self) -> serde_json::Value {
368 match self {
369 CopperlaceNumber::Integer(value) => serde_json::Value::Number(value.into()),
370 CopperlaceNumber::Unsigned(value) => serde_json::Value::Number(value.into()),
371 CopperlaceNumber::Float(value) => serde_json::Number::from_f64(value)
372 .map(serde_json::Value::Number)
373 .unwrap_or(serde_json::Value::Null),
374 }
375 }
376}