copperlace/render/
value.rs1use std::collections::BTreeMap;
2
3use serde::Serialize;
4
5use super::error::RenderError;
6use super::nodes::TextGeneratorNode;
7use super::state::RenderState;
8
9pub enum StructuredNode {
15 Object(BTreeMap<String, StructuredNode>),
17 Array(Vec<StructuredNode>),
19 Text(Box<dyn TextGeneratorNode>),
21 Number(CopperlaceNumber),
23 Boolean(bool),
25 Null,
27}
28
29impl StructuredNode {
30 pub(crate) fn generate_value(
31 &self,
32 state: &mut RenderState,
33 ) -> Result<CopperlaceValue, RenderError> {
34 match self {
35 StructuredNode::Object(values) => values
36 .iter()
37 .map(|(key, value)| Ok((key.clone(), value.generate_value(state)?)))
38 .collect::<Result<BTreeMap<_, _>, _>>()
39 .map(CopperlaceValue::Object),
40 StructuredNode::Array(values) => values
41 .iter()
42 .map(|value| value.generate_value(state))
43 .collect::<Result<Vec<_>, _>>()
44 .map(CopperlaceValue::Array),
45 StructuredNode::Text(node) => node.generate_text(state).map(CopperlaceValue::String),
46 StructuredNode::Number(value) => Ok(CopperlaceValue::Number(*value)),
47 StructuredNode::Boolean(value) => Ok(CopperlaceValue::Boolean(*value)),
48 StructuredNode::Null => Ok(CopperlaceValue::Null),
49 }
50 }
51}
52
53#[derive(Debug, Clone, PartialEq)]
55pub enum CopperlaceValue {
56 Object(BTreeMap<String, CopperlaceValue>),
58 Array(Vec<CopperlaceValue>),
60 String(String),
62 Number(CopperlaceNumber),
64 Boolean(bool),
66 Null,
68}
69
70impl CopperlaceValue {
71 pub fn into_json_value(self) -> serde_json::Value {
73 match self {
74 CopperlaceValue::Object(values) => serde_json::Value::Object(
75 values
76 .into_iter()
77 .map(|(key, value)| (key, value.into_json_value()))
78 .collect(),
79 ),
80 CopperlaceValue::Array(values) => serde_json::Value::Array(
81 values
82 .into_iter()
83 .map(CopperlaceValue::into_json_value)
84 .collect(),
85 ),
86 CopperlaceValue::String(value) => serde_json::Value::String(value),
87 CopperlaceValue::Number(value) => value.into_json_number(),
88 CopperlaceValue::Boolean(value) => serde_json::Value::Bool(value),
89 CopperlaceValue::Null => serde_json::Value::Null,
90 }
91 }
92
93 pub fn to_json_value(&self) -> serde_json::Value {
95 self.clone().into_json_value()
96 }
97
98 pub fn to_compact_json(&self) -> Result<String, RenderError> {
100 serde_json::to_string(&self.to_json_value())
101 .map_err(|error| RenderError::JsonSerialization(error.to_string()))
102 }
103
104 pub fn to_formatted_json(&self) -> Result<String, RenderError> {
106 let mut output = Vec::new();
107 let formatter = serde_json::ser::PrettyFormatter::with_indent(b"\t");
108 let mut serializer = serde_json::Serializer::with_formatter(&mut output, formatter);
109 self.to_json_value()
110 .serialize(&mut serializer)
111 .map_err(|error| RenderError::JsonSerialization(error.to_string()))?;
112 String::from_utf8(output).map_err(|error| RenderError::JsonSerialization(error.to_string()))
113 }
114}
115
116#[derive(Debug, Clone, Copy, PartialEq)]
118pub enum CopperlaceNumber {
119 Integer(i64),
121 Unsigned(u64),
123 Float(f64),
125}
126
127impl CopperlaceNumber {
128 pub(crate) fn from_json_number(number: serde_json::Number) -> Result<Self, RenderError> {
129 if let Some(value) = number.as_i64() {
130 return Ok(CopperlaceNumber::Integer(value));
131 }
132 if let Some(value) = number.as_u64() {
133 return Ok(CopperlaceNumber::Unsigned(value));
134 }
135 let Some(value) = number.as_f64() else {
136 return Err(RenderError::UnsupportedValue(
137 "number must be representable as i64, u64, or f64".to_string(),
138 ));
139 };
140 if !value.is_finite() {
141 return Err(RenderError::UnsupportedValue(
142 "number must be finite".to_string(),
143 ));
144 }
145 Ok(CopperlaceNumber::Float(value))
146 }
147
148 fn into_json_number(self) -> serde_json::Value {
149 match self {
150 CopperlaceNumber::Integer(value) => serde_json::Value::Number(value.into()),
151 CopperlaceNumber::Unsigned(value) => serde_json::Value::Number(value.into()),
152 CopperlaceNumber::Float(value) => serde_json::Number::from_f64(value)
153 .map(serde_json::Value::Number)
154 .unwrap_or(serde_json::Value::Null),
155 }
156 }
157}