1use minijinja::value::Value as MjValue;
7use serde::{Deserialize, Serialize};
8use std::collections::BTreeMap;
9
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15pub struct QuillValue(serde_json::Value);
16
17impl QuillValue {
18 pub fn from_toml(toml_val: &toml::Value) -> Result<Self, serde_json::Error> {
20 let json_val = serde_json::to_value(toml_val)?;
21 Ok(QuillValue(json_val))
22 }
23
24 pub fn from_yaml(yaml_val: serde_yaml::Value) -> Result<Self, serde_json::Error> {
26 let json_val = serde_json::to_value(&yaml_val)?;
27 Ok(QuillValue(json_val))
28 }
29
30 pub fn to_minijinja(&self) -> Result<MjValue, String> {
32 json_to_minijinja(&self.0)
33 }
34
35 pub fn as_json(&self) -> &serde_json::Value {
37 &self.0
38 }
39
40 pub fn into_json(self) -> serde_json::Value {
42 self.0
43 }
44
45 pub fn from_json(json_val: serde_json::Value) -> Self {
47 QuillValue(json_val)
48 }
49}
50
51fn json_to_minijinja(value: &serde_json::Value) -> Result<MjValue, String> {
53 use serde_json::Value as JsonValue;
54
55 let result = match value {
56 JsonValue::Null => MjValue::from(()),
57 JsonValue::Bool(b) => MjValue::from(*b),
58 JsonValue::Number(n) => {
59 if let Some(i) = n.as_i64() {
60 MjValue::from(i)
61 } else if let Some(u) = n.as_u64() {
62 MjValue::from(u)
63 } else if let Some(f) = n.as_f64() {
64 MjValue::from(f)
65 } else {
66 return Err("Invalid number in JSON".to_string());
67 }
68 }
69 JsonValue::String(s) => MjValue::from(s.clone()),
70 JsonValue::Array(arr) => {
71 let mut vec = Vec::new();
72 for item in arr {
73 vec.push(json_to_minijinja(item)?);
74 }
75 MjValue::from(vec)
76 }
77 JsonValue::Object(map) => {
78 let mut obj = BTreeMap::new();
79 for (k, v) in map {
80 obj.insert(k.clone(), json_to_minijinja(v)?);
81 }
82 MjValue::from_object(obj)
83 }
84 };
85
86 Ok(result)
87}
88
89impl QuillValue {
91 pub fn is_null(&self) -> bool {
93 self.0.is_null()
94 }
95
96 pub fn as_str(&self) -> Option<&str> {
98 self.0.as_str()
99 }
100
101 pub fn as_bool(&self) -> Option<bool> {
103 self.0.as_bool()
104 }
105
106 pub fn as_i64(&self) -> Option<i64> {
108 self.0.as_i64()
109 }
110
111 pub fn as_u64(&self) -> Option<u64> {
113 self.0.as_u64()
114 }
115
116 pub fn as_f64(&self) -> Option<f64> {
118 self.0.as_f64()
119 }
120
121 pub fn as_array(&self) -> Option<&Vec<serde_json::Value>> {
123 self.0.as_array()
124 }
125
126 pub fn as_sequence(&self) -> Option<&Vec<serde_json::Value>> {
128 self.0.as_array()
129 }
130
131 pub fn as_object(&self) -> Option<&serde_json::Map<String, serde_json::Value>> {
133 self.0.as_object()
134 }
135
136 pub fn as_mapping(&self) -> Option<&serde_json::Map<String, serde_json::Value>> {
138 self.0.as_object()
139 }
140
141 pub fn get(&self, key: &str) -> Option<QuillValue> {
143 self.0.get(key).map(|v| QuillValue(v.clone()))
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn test_from_toml() {
153 let toml_str = r#"
154 [package]
155 name = "test"
156 version = "1.0.0"
157 "#;
158 let toml_val: toml::Value = toml::from_str(toml_str).unwrap();
159 let quill_val = QuillValue::from_toml(&toml_val).unwrap();
160
161 assert!(quill_val.as_object().is_some());
162 }
163
164 #[test]
165 fn test_from_yaml() {
166 let yaml_str = r#"
167 title: Test Document
168 author: John Doe
169 count: 42
170 "#;
171 let yaml_val: serde_yaml::Value = serde_yaml::from_str(yaml_str).unwrap();
172 let quill_val = QuillValue::from_yaml(yaml_val).unwrap();
173
174 assert_eq!(
175 quill_val.get("title").as_ref().and_then(|v| v.as_str()),
176 Some("Test Document")
177 );
178 assert_eq!(
179 quill_val.get("author").as_ref().and_then(|v| v.as_str()),
180 Some("John Doe")
181 );
182 assert_eq!(
183 quill_val.get("count").as_ref().and_then(|v| v.as_i64()),
184 Some(42)
185 );
186 }
187
188 #[test]
189 fn test_to_minijinja() {
190 let json_val = serde_json::json!({
191 "title": "Test",
192 "count": 42,
193 "active": true,
194 "items": [1, 2, 3]
195 });
196 let quill_val = QuillValue::from_json(json_val);
197 let mj_val = quill_val.to_minijinja().unwrap();
198
199 assert!(mj_val.as_object().is_some());
201 }
202
203 #[test]
204 fn test_as_json() {
205 let json_val = serde_json::json!({"key": "value"});
206 let quill_val = QuillValue::from_json(json_val.clone());
207
208 assert_eq!(quill_val.as_json(), &json_val);
209 }
210
211 #[test]
212 fn test_into_json() {
213 let json_val = serde_json::json!({"key": "value"});
214 let quill_val = QuillValue::from_json(json_val.clone());
215
216 assert_eq!(quill_val.into_json(), json_val);
217 }
218
219 #[test]
220 fn test_delegating_methods() {
221 let quill_val = QuillValue::from_json(serde_json::json!({
222 "name": "test",
223 "count": 42,
224 "active": true,
225 "items": [1, 2, 3]
226 }));
227
228 assert_eq!(
229 quill_val.get("name").as_ref().and_then(|v| v.as_str()),
230 Some("test")
231 );
232 assert_eq!(
233 quill_val.get("count").as_ref().and_then(|v| v.as_i64()),
234 Some(42)
235 );
236 assert_eq!(
237 quill_val.get("active").as_ref().and_then(|v| v.as_bool()),
238 Some(true)
239 );
240 assert!(quill_val
241 .get("items")
242 .as_ref()
243 .and_then(|v| v.as_array())
244 .is_some());
245 }
246
247 #[test]
248 fn test_yaml_with_tags() {
249 let yaml_str = r#"
250 !tagged_value
251 value: 42
252 "#;
253 let yaml_val: serde_yaml::Value = serde_yaml::from_str(yaml_str).unwrap();
254 let quill_val = QuillValue::from_yaml(yaml_val).unwrap();
255
256 assert!(quill_val.as_object().is_some());
258 }
259
260 #[test]
261 fn test_null_value() {
262 let quill_val = QuillValue::from_json(serde_json::Value::Null);
263 assert!(quill_val.is_null());
264 }
265}