1use serde::{Deserialize, Serialize};
7use std::ops::Deref;
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14pub struct QuillValue(serde_json::Value);
15
16pub fn json_depth_exceeds(value: &serde_json::Value, max_depth: usize) -> bool {
27 use serde_json::Value;
28 let mut stack: Vec<(&Value, usize)> = vec![(value, 0)];
30 while let Some((v, depth)) = stack.pop() {
31 match v {
32 Value::Array(items) => {
33 if depth + 1 > max_depth && !items.is_empty() {
34 return true;
35 }
36 stack.extend(items.iter().map(|c| (c, depth + 1)));
37 }
38 Value::Object(map) => {
39 if depth + 1 > max_depth && !map.is_empty() {
40 return true;
41 }
42 stack.extend(map.values().map(|c| (c, depth + 1)));
43 }
44 _ => {}
45 }
46 }
47 false
48}
49
50impl QuillValue {
51 pub fn from_yaml_str(yaml_str: &str) -> Result<Self, serde_saphyr::Error> {
53 let json_val: serde_json::Value = serde_saphyr::from_str(yaml_str)?;
54 Ok(QuillValue(json_val))
55 }
56
57 pub fn as_json(&self) -> &serde_json::Value {
59 &self.0
60 }
61
62 pub fn into_json(self) -> serde_json::Value {
64 self.0
65 }
66
67 pub fn from_json(json_val: serde_json::Value) -> Self {
69 QuillValue(json_val)
70 }
71
72 pub fn string(s: impl Into<String>) -> Self {
74 QuillValue(serde_json::Value::String(s.into()))
75 }
76
77 pub fn integer(n: i64) -> Self {
79 QuillValue(serde_json::Value::Number(n.into()))
80 }
81
82 pub fn bool(b: bool) -> Self {
84 QuillValue(serde_json::Value::Bool(b))
85 }
86
87 pub fn null() -> Self {
89 QuillValue(serde_json::Value::Null)
90 }
91}
92
93impl Deref for QuillValue {
94 type Target = serde_json::Value;
95
96 fn deref(&self) -> &Self::Target {
97 &self.0
98 }
99}
100
101impl QuillValue {
103 pub fn is_null(&self) -> bool {
105 self.0.is_null()
106 }
107
108 pub fn as_str(&self) -> Option<&str> {
110 self.0.as_str()
111 }
112
113 pub fn as_bool(&self) -> Option<bool> {
115 self.0.as_bool()
116 }
117
118 pub fn as_i64(&self) -> Option<i64> {
120 self.0.as_i64()
121 }
122
123 pub fn as_u64(&self) -> Option<u64> {
125 self.0.as_u64()
126 }
127
128 pub fn as_f64(&self) -> Option<f64> {
130 self.0.as_f64()
131 }
132
133 pub fn as_array(&self) -> Option<&Vec<serde_json::Value>> {
135 self.0.as_array()
136 }
137
138 pub fn as_object(&self) -> Option<&serde_json::Map<String, serde_json::Value>> {
140 self.0.as_object()
141 }
142
143 pub fn get(&self, key: &str) -> Option<QuillValue> {
145 self.0.get(key).map(|v| QuillValue(v.clone()))
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 #[test]
154 fn test_from_yaml_value() {
155 let yaml_str = r#"
156 package:
157 name: test
158 version: 1.0.0
159 "#;
160 let json_val: serde_json::Value = serde_saphyr::from_str(yaml_str).unwrap();
161 let quill_val = QuillValue::from_json(json_val);
162
163 assert!(quill_val.as_object().is_some());
164 assert_eq!(
165 quill_val
166 .get("package")
167 .unwrap()
168 .get("name")
169 .unwrap()
170 .as_str(),
171 Some("test")
172 );
173 }
174
175 #[test]
176 fn test_from_yaml_str() {
177 let yaml_str = r#"
178 title: Test Document
179 author: John Doe
180 count: 42
181 "#;
182 let quill_val = QuillValue::from_yaml_str(yaml_str).unwrap();
183
184 assert_eq!(
185 quill_val.get("title").as_ref().and_then(|v| v.as_str()),
186 Some("Test Document")
187 );
188 assert_eq!(
189 quill_val.get("author").as_ref().and_then(|v| v.as_str()),
190 Some("John Doe")
191 );
192 assert_eq!(
193 quill_val.get("count").as_ref().and_then(|v| v.as_i64()),
194 Some(42)
195 );
196 }
197
198 #[test]
199 fn test_delegating_methods() {
200 let quill_val = QuillValue::from_json(serde_json::json!({
201 "name": "test",
202 "count": 42,
203 "active": true,
204 "items": [1, 2, 3]
205 }));
206
207 assert_eq!(
208 quill_val.get("name").as_ref().and_then(|v| v.as_str()),
209 Some("test")
210 );
211 assert_eq!(
212 quill_val.get("count").as_ref().and_then(|v| v.as_i64()),
213 Some(42)
214 );
215 assert_eq!(
216 quill_val.get("active").as_ref().and_then(|v| v.as_bool()),
217 Some(true)
218 );
219 assert!(quill_val
220 .get("items")
221 .as_ref()
222 .and_then(|v| v.as_array())
223 .is_some());
224 }
225
226 #[test]
227 fn test_yaml_custom_tags_ignored_at_value_level() {
228 let yaml_str = "memo_from: !fill 2d lt example";
234 let quill_val = QuillValue::from_yaml_str(yaml_str).unwrap();
235
236 assert_eq!(
237 quill_val.get("memo_from").as_ref().and_then(|v| v.as_str()),
238 Some("2d lt example")
239 );
240 }
241}
242