ankurah_core/property/value/
json.rs1use serde::{Deserialize, Serialize};
19#[cfg(feature = "wasm")]
20use wasm_bindgen::prelude::*;
21
22use crate::property::{traits::PropertyError, Property};
23use crate::value::Value;
24
25#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
31#[serde(transparent)]
32pub struct Json(pub serde_json::Value);
33
34impl Json {
35 pub fn new(value: serde_json::Value) -> Self { Json(value) }
37
38 pub fn null() -> Self { Json(serde_json::Value::Null) }
40
41 pub fn object(pairs: impl IntoIterator<Item = (impl Into<String>, serde_json::Value)>) -> Self {
43 let map: serde_json::Map<String, serde_json::Value> = pairs.into_iter().map(|(k, v)| (k.into(), v)).collect();
44 Json(serde_json::Value::Object(map))
45 }
46
47 pub fn array(items: impl IntoIterator<Item = serde_json::Value>) -> Self { Json(serde_json::Value::Array(items.into_iter().collect())) }
49
50 pub fn inner(&self) -> &serde_json::Value { &self.0 }
52
53 pub fn inner_mut(&mut self) -> &mut serde_json::Value { &mut self.0 }
55
56 pub fn into_inner(self) -> serde_json::Value { self.0 }
58
59 pub fn get_path(&self, path: &[&str]) -> Option<&serde_json::Value> {
63 let mut current = &self.0;
64 for step in path {
65 current = current.get(*step)?;
66 }
67 Some(current)
68 }
69
70 pub fn is_null(&self) -> bool { self.0.is_null() }
72
73 pub fn is_object(&self) -> bool { self.0.is_object() }
75
76 pub fn is_array(&self) -> bool { self.0.is_array() }
78}
79
80impl Default for Json {
81 fn default() -> Self { Json::null() }
82}
83
84impl From<serde_json::Value> for Json {
85 fn from(value: serde_json::Value) -> Self { Json(value) }
86}
87
88impl From<Json> for serde_json::Value {
89 fn from(json: Json) -> Self { json.0 }
90}
91
92impl std::ops::Deref for Json {
93 type Target = serde_json::Value;
94
95 fn deref(&self) -> &Self::Target { &self.0 }
96}
97
98impl std::ops::DerefMut for Json {
99 fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
100}
101
102#[cfg(feature = "wasm")]
106#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
107const TS_JSON_TYPE: &'static str = r#"
108/** A JSON value - can be any valid JSON: object, array, string, number, boolean, or null */
109export type Json = any;
110"#;
111
112#[cfg(feature = "wasm")]
113impl From<Json> for JsValue {
114 fn from(json: Json) -> Self {
115 let serializer = serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true);
118 json.0.serialize(&serializer).unwrap_or(JsValue::NULL)
119 }
120}
121
122#[cfg(feature = "wasm")]
123impl wasm_bindgen::describe::WasmDescribe for Json {
124 fn describe() { JsValue::describe() }
125}
126
127#[cfg(feature = "wasm")]
128impl wasm_bindgen::convert::IntoWasmAbi for Json {
129 type Abi = <JsValue as wasm_bindgen::convert::IntoWasmAbi>::Abi;
130
131 fn into_abi(self) -> Self::Abi { JsValue::from(self).into_abi() }
132}
133
134#[cfg(feature = "wasm")]
135impl wasm_bindgen::convert::FromWasmAbi for Json {
136 type Abi = <JsValue as wasm_bindgen::convert::FromWasmAbi>::Abi;
137
138 unsafe fn from_abi(js: Self::Abi) -> Self {
139 let js_value = JsValue::from_abi(js);
140 let value: serde_json::Value = serde_wasm_bindgen::from_value(js_value).unwrap_or(serde_json::Value::Null);
142 Json(value)
143 }
144}
145
146#[cfg(feature = "uniffi")]
149::uniffi::custom_type!(Json, String, {
150 lower: |obj| serde_json::to_string(&obj.0).expect("Failed to serialize JSON"),
151 try_lift: |val| serde_json::from_str(&val).map(Json).map_err(Into::into),
152});
153
154impl Property for Json {
155 fn into_value(&self) -> Result<Option<Value>, PropertyError> { Ok(Some(Value::Json(self.0.clone()))) }
156
157 fn from_value(value: Option<Value>) -> Result<Self, PropertyError> {
158 match value {
159 Some(Value::Json(json)) => Ok(Json(json)),
160 Some(Value::Binary(bytes)) => {
161 let json_value: serde_json::Value =
163 serde_json::from_slice(&bytes).map_err(|e| PropertyError::DeserializeError(Box::new(e)))?;
164 Ok(Json(json_value))
165 }
166 Some(other) => Err(PropertyError::InvalidVariant { given: other, ty: "Json".to_string() }),
167 None => Err(PropertyError::Missing),
168 }
169 }
170}
171
172#[macro_export]
187macro_rules! json {
188 ($($json:tt)+) => {
189 $crate::property::value::json::Json::new(serde_json::json!($($json)+))
190 };
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 #[test]
198 fn test_json_roundtrip() {
199 let original = Json::object([
200 ("name".to_string(), serde_json::json!("test")),
201 ("count".to_string(), serde_json::json!(42)),
202 (
203 "nested".to_string(),
204 serde_json::json!({
205 "inner": "value"
206 }),
207 ),
208 ]);
209
210 let value = original.into_value().unwrap().unwrap();
212 let recovered = Json::from_value(Some(value)).unwrap();
213
214 assert_eq!(original, recovered);
215 }
216
217 #[test]
218 fn test_json_get_path() {
219 let json = Json::new(serde_json::json!({
220 "licensing": {
221 "territory": "US",
222 "rights": {
223 "holder": "Label"
224 }
225 }
226 }));
227
228 assert_eq!(json.get_path(&["licensing", "territory"]), Some(&serde_json::json!("US")));
229 assert_eq!(json.get_path(&["licensing", "rights", "holder"]), Some(&serde_json::json!("Label")));
230 assert_eq!(json.get_path(&["licensing", "nonexistent"]), None);
231 assert_eq!(json.get_path(&["nonexistent"]), None);
232 }
233
234 #[test]
235 fn test_json_null() {
236 let json = Json::null();
237 assert!(json.is_null());
238
239 let value = json.into_value().unwrap().unwrap();
240 let recovered = Json::from_value(Some(value)).unwrap();
241 assert!(recovered.is_null());
242 }
243
244 #[test]
245 fn test_json_missing() {
246 let result = Json::from_value(None);
247 assert!(matches!(result, Err(PropertyError::Missing)));
248 }
249
250 #[test]
251 fn test_json_invalid_variant() {
252 let result = Json::from_value(Some(Value::String("not json bytes".to_string())));
253 assert!(matches!(result, Err(PropertyError::InvalidVariant { .. })));
254 }
255
256 #[test]
257 fn test_json_deref() {
258 let json = Json::new(serde_json::json!({"key": "value"}));
259
260 assert!(json.is_object());
262 assert_eq!(json.get("key"), Some(&serde_json::json!("value")));
263 }
264}