facet_value/
macros.rs

1//! Macros for constructing `Value` instances.
2
3/// Creates a [`Value`](crate::Value) from a JSON-like syntax.
4///
5/// # Examples
6///
7/// ```
8/// use facet_value::{Value, value};
9///
10/// // Null
11/// let v = value!(null);
12/// assert!(v.is_null());
13///
14/// // Booleans
15/// let v = value!(true);
16/// assert_eq!(v.as_bool(), Some(true));
17///
18/// // Numbers
19/// let v = value!(42);
20/// assert_eq!(v.as_number().unwrap().to_i64(), Some(42));
21///
22/// // Strings
23/// let v = value!("hello");
24/// assert_eq!(v.as_string().unwrap().as_str(), "hello");
25///
26/// // Arrays
27/// let v = value!([1, 2, 3]);
28/// assert_eq!(v.as_array().unwrap().len(), 3);
29///
30/// // Objects
31/// let v = value!({
32///     "name": "Alice",
33///     "age": 30,
34///     "active": true
35/// });
36/// assert_eq!(v.as_object().unwrap().get("name").unwrap().as_string().unwrap().as_str(), "Alice");
37///
38/// // Nested structures
39/// let v = value!({
40///     "users": [
41///         { "name": "Alice", "age": 30 },
42///         { "name": "Bob", "age": 25 }
43///     ],
44///     "count": 2
45/// });
46/// ```
47///
48/// # Variable interpolation
49///
50/// You can interpolate variables using parentheses:
51///
52/// ```
53/// use facet_value::{Value, value};
54///
55/// let name = "Alice";
56/// let age = 30;
57///
58/// let v = value!({
59///     "name": (name),
60///     "age": (age)
61/// });
62/// ```
63#[macro_export]
64macro_rules! value {
65    // Null
66    (null) => {
67        $crate::Value::NULL
68    };
69
70    // Boolean true
71    (true) => {
72        $crate::Value::TRUE
73    };
74
75    // Boolean false
76    (false) => {
77        $crate::Value::FALSE
78    };
79
80    // Empty array
81    ([]) => {
82        $crate::Value::from($crate::VArray::new())
83    };
84
85    // Array with elements
86    ([ $($elem:tt),* $(,)? ]) => {{
87        let mut arr = $crate::VArray::new();
88        $(
89            arr.push($crate::value!($elem));
90        )*
91        $crate::Value::from(arr)
92    }};
93
94    // Empty object
95    ({}) => {
96        $crate::Value::from($crate::VObject::new())
97    };
98
99    // Object with key-value pairs
100    ({ $($key:tt : $value:tt),* $(,)? }) => {{
101        let mut obj = $crate::VObject::new();
102        $(
103            obj.insert($key, $crate::value!($value));
104        )*
105        $crate::Value::from(obj)
106    }};
107
108    // Parenthesized expression (variable interpolation)
109    (( $expr:expr )) => {
110        $crate::Value::from($expr)
111    };
112
113    // Literal expression (numbers, strings, etc.)
114    ($other:expr) => {
115        $crate::Value::from($other)
116    };
117}
118
119#[cfg(test)]
120mod tests {
121    use crate::{VArray, Value};
122
123    #[test]
124    fn test_null() {
125        let v = value!(null);
126        assert!(v.is_null());
127    }
128
129    #[test]
130    fn test_booleans() {
131        assert_eq!(value!(true), Value::TRUE);
132        assert_eq!(value!(false), Value::FALSE);
133    }
134
135    #[test]
136    fn test_numbers() {
137        let v = value!(42);
138        assert_eq!(v.as_number().unwrap().to_i64(), Some(42));
139
140        let v = value!(-17);
141        assert_eq!(v.as_number().unwrap().to_i64(), Some(-17));
142
143        let v = value!(1.5);
144        assert!((v.as_number().unwrap().to_f64().unwrap() - 1.5).abs() < 0.001);
145    }
146
147    #[test]
148    fn test_strings() {
149        let v = value!("hello");
150        assert_eq!(v.as_string().unwrap().as_str(), "hello");
151
152        let v = value!("hello world with spaces");
153        assert_eq!(v.as_string().unwrap().as_str(), "hello world with spaces");
154    }
155
156    #[test]
157    fn test_empty_array() {
158        let v = value!([]);
159        assert!(v.is_array());
160        assert!(v.as_array().unwrap().is_empty());
161    }
162
163    #[test]
164    fn test_array_of_numbers() {
165        let v = value!([1, 2, 3]);
166        let arr = v.as_array().unwrap();
167        assert_eq!(arr.len(), 3);
168        assert_eq!(arr[0].as_number().unwrap().to_i64(), Some(1));
169        assert_eq!(arr[1].as_number().unwrap().to_i64(), Some(2));
170        assert_eq!(arr[2].as_number().unwrap().to_i64(), Some(3));
171    }
172
173    #[test]
174    fn test_array_mixed_types() {
175        let v = value!([1, "two", true, null]);
176        let arr = v.as_array().unwrap();
177        assert_eq!(arr.len(), 4);
178        assert_eq!(arr[0].as_number().unwrap().to_i64(), Some(1));
179        assert_eq!(arr[1].as_string().unwrap().as_str(), "two");
180        assert_eq!(arr[2].as_bool(), Some(true));
181        assert!(arr[3].is_null());
182    }
183
184    #[test]
185    fn test_empty_object() {
186        let v = value!({});
187        assert!(v.is_object());
188        assert!(v.as_object().unwrap().is_empty());
189    }
190
191    #[test]
192    fn test_object_simple() {
193        let v = value!({
194            "name": "Alice",
195            "age": 30,
196            "active": true
197        });
198        let obj = v.as_object().unwrap();
199        assert_eq!(obj.len(), 3);
200        assert_eq!(obj["name"].as_string().unwrap().as_str(), "Alice");
201        assert_eq!(obj["age"].as_number().unwrap().to_i64(), Some(30));
202        assert_eq!(obj["active"].as_bool(), Some(true));
203    }
204
205    #[test]
206    fn test_nested_structure() {
207        let v = value!({
208            "users": [
209                { "name": "Alice", "age": 30 },
210                { "name": "Bob", "age": 25 }
211            ],
212            "count": 2
213        });
214
215        let obj = v.as_object().unwrap();
216        assert_eq!(obj["count"].as_number().unwrap().to_i64(), Some(2));
217
218        let users = obj["users"].as_array().unwrap();
219        assert_eq!(users.len(), 2);
220
221        let alice = users[0].as_object().unwrap();
222        assert_eq!(alice["name"].as_string().unwrap().as_str(), "Alice");
223        assert_eq!(alice["age"].as_number().unwrap().to_i64(), Some(30));
224
225        let bob = users[1].as_object().unwrap();
226        assert_eq!(bob["name"].as_string().unwrap().as_str(), "Bob");
227        assert_eq!(bob["age"].as_number().unwrap().to_i64(), Some(25));
228    }
229
230    #[test]
231    fn test_variable_interpolation() {
232        let name = "Alice";
233        let age = 30i64;
234
235        let v = value!({
236            "name": (name),
237            "age": (age)
238        });
239
240        let obj = v.as_object().unwrap();
241        assert_eq!(obj["name"].as_string().unwrap().as_str(), "Alice");
242        assert_eq!(obj["age"].as_number().unwrap().to_i64(), Some(30));
243    }
244
245    #[test]
246    fn test_array_interpolation() {
247        let items = vec![1i64, 2, 3];
248        let arr: VArray = items.into_iter().collect();
249
250        let v = value!({
251            "items": (arr)
252        });
253
254        let obj = v.as_object().unwrap();
255        assert_eq!(obj["items"].as_array().unwrap().len(), 3);
256    }
257
258    #[test]
259    fn test_trailing_comma() {
260        // Arrays with trailing comma
261        let v = value!([1, 2, 3,]);
262        assert_eq!(v.as_array().unwrap().len(), 3);
263
264        // Objects with trailing comma
265        let v = value!({
266            "a": 1,
267            "b": 2,
268        });
269        assert_eq!(v.as_object().unwrap().len(), 2);
270    }
271
272    #[test]
273    fn test_deeply_nested() {
274        let v = value!({
275            "level1": {
276                "level2": {
277                    "level3": {
278                        "data": [1, 2, 3]
279                    }
280                }
281            }
282        });
283
284        let data = &v.as_object().unwrap()["level1"].as_object().unwrap()["level2"]
285            .as_object()
286            .unwrap()["level3"]
287            .as_object()
288            .unwrap()["data"];
289
290        assert_eq!(data.as_array().unwrap().len(), 3);
291    }
292}