Skip to main content

stepflow_flow/values/
value_ref.rs

1// Copyright 2025 DataStax Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4// in compliance with the License. You may obtain a copy of the License at
5//
6//     http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software distributed under the License
9// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10// or implied. See the License for the specific language governing permissions and limitations under
11// the License.
12
13use owning_ref::ArcRef;
14use serde::{Deserialize, Serialize};
15use std::sync::Arc;
16
17/// A literal value which may be passed to a component.
18///
19/// This is a reference to a value owned by an `Arc<serde_json::Value>`.
20///
21/// The value is projected to a subfield of the `Arc` when accessed.
22///
23/// This is useful for avoiding cloning the value when accessing nested fields.
24///
25/// The value is projected to a subfield of the `Arc` when accessed.
26// TODO: Look at expanding ValueRef representaiton to be an explicit enum
27// allowing expansion of templates without duplicating literal sub-trees.
28#[derive(Clone, PartialEq, Hash, Eq)]
29#[repr(transparent)]
30pub struct ValueRef<T: 'static = serde_json::Value>(ArcRef<'static, serde_json::Value, T>);
31
32impl<T: std::fmt::Debug> std::fmt::Debug for ValueRef<T> {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        self.0.fmt(f)
35    }
36}
37
38impl Default for ValueRef {
39    fn default() -> Self {
40        Self(ArcRef::new(Arc::new(serde_json::Value::Null)))
41    }
42}
43
44impl schemars::JsonSchema for ValueRef {
45    fn schema_name() -> std::borrow::Cow<'static, str> {
46        "Value".into()
47    }
48
49    fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
50        schemars::json_schema!({
51            "description": "Any JSON value (object, array, string, number, boolean, or null)"
52        })
53    }
54}
55
56impl Serialize for ValueRef {
57    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
58    where
59        S: serde::Serializer,
60    {
61        self.0.as_ref().serialize(serializer)
62    }
63}
64
65impl<'de> Deserialize<'de> for ValueRef {
66    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
67    where
68        D: serde::Deserializer<'de>,
69    {
70        serde_json::Value::deserialize(deserializer).map(Self::new)
71    }
72}
73
74impl<T: Into<serde_json::Value>> From<T> for ValueRef {
75    fn from(value: T) -> Self {
76        Self::new(value.into())
77    }
78}
79
80impl ValueRef<serde_json::Value> {
81    pub fn new(value: serde_json::Value) -> Self {
82        Self(ArcRef::new(Arc::new(value)))
83    }
84
85    /// Return a redacted version of this value ref for printing.
86    pub fn redacted<'a>(
87        &'a self,
88        secrets: &'a crate::values::Secrets,
89    ) -> crate::values::RedactedValue<'a> {
90        secrets.redacted(self.value())
91    }
92
93    pub fn is_truthy(&self) -> bool {
94        match self.0.as_ref() {
95            serde_json::Value::Bool(b) => *b,
96            serde_json::Value::Number(n) => {
97                if let Some(n) = n.as_u64() {
98                    n != 0
99                } else if let Some(n) = n.as_i64() {
100                    n != 0
101                } else {
102                    n.as_f64().unwrap() != 0.0
103                }
104            }
105            serde_json::Value::String(s) => !s.is_empty(),
106            _ => true,
107        }
108    }
109
110    /// Access an object field by name.
111    pub fn path(&self, path: &str) -> Option<ValueRef> {
112        match self.0.as_ref() {
113            serde_json::Value::Object(obj) => obj
114                .get(path)
115                .map(|v| ValueRef(project_to_subfield(self.0.clone(), v))),
116            _ => None,
117        }
118    }
119
120    /// Access an array element by index.
121    pub fn index(&self, index: usize) -> Option<ValueRef> {
122        match self.0.as_ref() {
123            serde_json::Value::Array(arr) => arr
124                .get(index)
125                .map(|v| ValueRef(project_to_subfield(self.0.clone(), v))),
126            _ => None,
127        }
128    }
129
130    /// Access value using a JSON path
131    pub fn resolve_json_path(&self, json_path: &crate::workflow::JsonPath) -> Option<ValueRef> {
132        use crate::workflow::PathPart;
133
134        let mut current = self.clone();
135
136        for part in json_path.parts() {
137            match part {
138                PathPart::Field(field_name) => {
139                    current = current.path(field_name)?;
140                }
141                PathPart::Index(index) => {
142                    current = current.index(*index)?;
143                }
144                PathPart::IndexStr(index_str) => {
145                    current = current.path(index_str)?;
146                }
147            }
148        }
149
150        Some(current)
151    }
152
153    /// Cast to an object if this value is an object
154    pub fn as_object(&self) -> Option<ValueRef<serde_json::Map<String, serde_json::Value>>> {
155        match self.0.as_ref() {
156            serde_json::Value::Object(obj) => {
157                Some(ValueRef(project_to_subfield(self.0.clone(), obj)))
158            }
159            _ => None,
160        }
161    }
162
163    /// Cast to an array if this value is an array
164    pub fn as_array(&self) -> Option<ValueRef<Vec<serde_json::Value>>> {
165        match self.0.as_ref() {
166            serde_json::Value::Array(arr) => {
167                Some(ValueRef(project_to_subfield(self.0.clone(), arr)))
168            }
169            _ => None,
170        }
171    }
172
173    pub fn value(&self) -> &serde_json::Value {
174        self.0.as_ref()
175    }
176
177    /// Clone the underlying JSON value
178    pub fn clone_value(&self) -> serde_json::Value {
179        self.0.as_ref().clone()
180    }
181
182    /// Deserialize the value into a specific type
183    pub fn deserialize<T>(&self) -> Result<T, serde_json::Error>
184    where
185        T: serde::de::DeserializeOwned,
186    {
187        serde_json::from_value(self.as_ref().clone())
188    }
189
190    /// Get the value as a boolean if it is one
191    pub fn as_bool(&self) -> Option<bool> {
192        match self.0.as_ref() {
193            serde_json::Value::Bool(b) => Some(*b),
194            _ => None,
195        }
196    }
197
198    /// Get the value as a string if it is one
199    pub fn as_str(&self) -> Option<&str> {
200        match self.0.as_ref() {
201            serde_json::Value::String(s) => Some(s.as_str()),
202            _ => None,
203        }
204    }
205
206    /// Get the value as a number if it is one
207    pub fn as_number(&self) -> Option<&serde_json::Number> {
208        match self.0.as_ref() {
209            serde_json::Value::Number(n) => Some(n),
210            _ => None,
211        }
212    }
213
214    /// Check if the value is null
215    pub fn is_null(&self) -> bool {
216        matches!(self.0.as_ref(), serde_json::Value::Null)
217    }
218}
219
220impl ValueRef<Vec<serde_json::Value>> {
221    pub fn iter(&self) -> impl Iterator<Item = ValueRef> + '_ {
222        (0..self.0.len()).map(move |i| ValueRef(self.0.clone().map(move |vec| &vec[i])))
223    }
224
225    pub fn get(&self, index: usize) -> Option<ValueRef> {
226        if index < self.0.len() {
227            Some(ValueRef(self.0.clone().map(move |vec| &vec[index])))
228        } else {
229            None
230        }
231    }
232
233    pub fn len(&self) -> usize {
234        self.0.len()
235    }
236
237    pub fn is_empty(&self) -> bool {
238        self.0.is_empty()
239    }
240}
241
242// Helper function to project an ArcRef to a subfield
243// SAFETY: This is safe because we're projecting to a subfield of the same object
244// that the ArcRef already owns, and the lifetime is constrained appropriately.
245fn project_to_subfield<C, T1, T2>(arc: ArcRef<'static, C, T1>, value: &T2) -> ArcRef<'static, C, T2>
246where
247    T1: 'static,
248    T2: 'static,
249{
250    let ptr = value as *const T2;
251    // SAFETY: The pointer is valid because it points to a subfield of the object
252    // that ArcRef owns, and T2: 'static ensures the subfield has the right lifetime
253    arc.map(move |_| unsafe { &*ptr })
254}
255
256impl ValueRef<serde_json::Map<String, serde_json::Value>> {
257    pub fn iter(&self) -> impl Iterator<Item = (&str, ValueRef)> + '_ {
258        self.0.iter().map(|(k, v)| {
259            let k = k.as_str();
260            (k, ValueRef(project_to_subfield(self.0.clone(), v)))
261        })
262    }
263
264    pub fn get(&self, key: &str) -> Option<ValueRef> {
265        self.0
266            .get(key)
267            .map(|v| ValueRef(project_to_subfield(self.0.clone(), v)))
268    }
269
270    pub fn contains_key(&self, key: &str) -> bool {
271        self.0.contains_key(key)
272    }
273
274    pub fn len(&self) -> usize {
275        self.0.len()
276    }
277
278    pub fn is_empty(&self) -> bool {
279        self.0.is_empty()
280    }
281
282    pub fn keys(&self) -> impl Iterator<Item = &str> {
283        self.0.keys().map(|s| s.as_str())
284    }
285}
286
287// Implement AsRef trait for compatibility with existing code
288impl AsRef<serde_json::Value> for ValueRef {
289    fn as_ref(&self) -> &serde_json::Value {
290        &self.0
291    }
292}
293
294#[cfg(test)]
295mod tests {
296    use std::f64::consts::PI;
297
298    use super::*;
299    use serde_json::json;
300
301    #[test]
302    fn test_value_ref_new() {
303        let value = json!({"key": "value"});
304        let value_ref = ValueRef::new(value.clone());
305        assert_eq!(value_ref.as_ref(), &value);
306    }
307
308    #[test]
309    fn test_value_ref_path_object() {
310        let value = json!({
311            "name": "Alice",
312            "age": 30,
313            "nested": {
314                "city": "San Francisco"
315            }
316        });
317        let value_ref = ValueRef::new(value);
318
319        // Test accessing top-level field
320        let name = value_ref.path("name").unwrap();
321        assert_eq!(name.as_ref(), &json!("Alice"));
322
323        // Test accessing nested object
324        let nested = value_ref.path("nested").unwrap();
325        assert_eq!(nested.as_ref(), &json!({"city": "San Francisco"}));
326
327        // Test accessing nested field through path of nested object
328        let city = nested.path("city").unwrap();
329        assert_eq!(city.as_ref(), &json!("San Francisco"));
330
331        // Test non-existent field
332        assert!(value_ref.path("nonexistent").is_none());
333    }
334
335    #[test]
336    fn test_value_ref_path_array() {
337        let value = json!(["first", "second", {"nested": "value"}]);
338        let value_ref = ValueRef::new(value);
339
340        // Test accessing array elements by index
341        let first = value_ref.index(0).unwrap();
342        assert_eq!(first.as_ref(), &json!("first"));
343
344        let second = value_ref.index(1).unwrap();
345        assert_eq!(second.as_ref(), &json!("second"));
346
347        let third = value_ref.index(2).unwrap();
348        assert_eq!(third.as_ref(), &json!({"nested": "value"}));
349
350        // Test accessing nested field in array element
351        let nested = third.path("nested").unwrap();
352        assert_eq!(nested.as_ref(), &json!("value"));
353
354        // Test out of bounds
355        assert!(value_ref.index(10).is_none());
356    }
357
358    #[test]
359    fn test_value_ref_as_object() {
360        let value = json!({
361            "key1": "value1",
362            "key2": "value2"
363        });
364        let value_ref = ValueRef::new(value);
365
366        let obj = value_ref.as_object().unwrap();
367        assert_eq!(obj.len(), 2);
368        assert!(obj.contains_key("key1"));
369        assert!(obj.contains_key("key2"));
370
371        let val1 = obj.get("key1").unwrap();
372        assert_eq!(val1.as_ref(), &json!("value1"));
373
374        // Test keys iterator
375        let keys: Vec<&str> = obj.keys().collect();
376        assert!(keys.contains(&"key1"));
377        assert!(keys.contains(&"key2"));
378
379        // Test iter
380        let items: Vec<_> = obj.iter().collect();
381        assert_eq!(items.len(), 2);
382    }
383
384    #[test]
385    fn test_value_ref_as_array() {
386        let value = json!(["item1", "item2", "item3"]);
387        let value_ref = ValueRef::new(value);
388
389        let arr = value_ref.as_array().unwrap();
390        assert_eq!(arr.len(), 3);
391        assert!(!arr.is_empty());
392
393        let item1 = arr.get(0).unwrap();
394        assert_eq!(item1.as_ref(), &json!("item1"));
395
396        let item2 = arr.get(1).unwrap();
397        assert_eq!(item2.as_ref(), &json!("item2"));
398
399        // Test out of bounds
400        assert!(arr.get(10).is_none());
401
402        // Test iterator
403        let items: Vec<_> = arr.iter().collect();
404        assert_eq!(items.len(), 3);
405        assert_eq!(items[0].as_ref(), &json!("item1"));
406        assert_eq!(items[1].as_ref(), &json!("item2"));
407        assert_eq!(items[2].as_ref(), &json!("item3"));
408    }
409
410    #[test]
411    fn test_value_ref_is_truthy() {
412        assert!(ValueRef::new(json!(true)).is_truthy());
413        assert!(!ValueRef::new(json!(false)).is_truthy());
414
415        assert!(ValueRef::new(json!(1)).is_truthy());
416        assert!(ValueRef::new(json!(-1)).is_truthy());
417        assert!(ValueRef::new(json!(1.5)).is_truthy());
418        assert!(!ValueRef::new(json!(0)).is_truthy());
419        assert!(!ValueRef::new(json!(0.0)).is_truthy());
420
421        assert!(ValueRef::new(json!("hello")).is_truthy());
422        assert!(!ValueRef::new(json!("")).is_truthy());
423
424        assert!(ValueRef::new(json!(null)).is_truthy());
425        assert!(ValueRef::new(json!({})).is_truthy());
426        assert!(ValueRef::new(json!([])).is_truthy());
427    }
428
429    #[test]
430    fn test_value_ref_deserialize() {
431        use serde::{Deserialize, Serialize};
432
433        #[derive(Debug, PartialEq, Serialize, Deserialize)]
434        struct TestStruct {
435            name: String,
436            age: u32,
437        }
438
439        let data = TestStruct {
440            name: "Alice".to_string(),
441            age: 30,
442        };
443        let value = serde_json::to_value(&data).unwrap();
444        let value_ref = ValueRef::new(value);
445
446        let deserialized: TestStruct = value_ref.deserialize().unwrap();
447        assert_eq!(deserialized, data);
448
449        // Test deserialization error
450        let invalid_value = ValueRef::new(json!("not a struct"));
451        let result: Result<TestStruct, _> = invalid_value.deserialize();
452        assert!(result.is_err());
453    }
454
455    #[test]
456    fn test_value_ref_type_accessors() {
457        // Test as_bool
458        assert_eq!(ValueRef::new(json!(true)).as_bool(), Some(true));
459        assert_eq!(ValueRef::new(json!(false)).as_bool(), Some(false));
460        assert_eq!(ValueRef::new(json!("hello")).as_bool(), None);
461        assert_eq!(ValueRef::new(json!(42)).as_bool(), None);
462
463        // Test as_str
464        assert_eq!(ValueRef::new(json!("hello")).as_str(), Some("hello"));
465        assert_eq!(ValueRef::new(json!("")).as_str(), Some(""));
466        assert_eq!(ValueRef::new(json!(42)).as_str(), None);
467        assert_eq!(ValueRef::new(json!(true)).as_str(), None);
468
469        // Test as_number
470        let num_value = ValueRef::new(json!(42));
471        assert!(num_value.as_number().is_some());
472        assert_eq!(num_value.as_number().unwrap().as_u64(), Some(42));
473
474        let float_value = ValueRef::new(json!(PI));
475        assert!(float_value.as_number().is_some());
476        assert_eq!(float_value.as_number().unwrap().as_f64(), Some(PI));
477
478        assert_eq!(ValueRef::new(json!("hello")).as_number(), None);
479        assert_eq!(ValueRef::new(json!(true)).as_number(), None);
480
481        // Test is_null
482        assert!(ValueRef::new(json!(null)).is_null());
483        assert!(!ValueRef::new(json!(0)).is_null());
484        assert!(!ValueRef::new(json!("")).is_null());
485        assert!(!ValueRef::new(json!(false)).is_null());
486    }
487
488    // Helper function to assert that two ValueRefs share the same underlying Arc
489    fn assert_same_base<T1, T2>(base: &ValueRef<T1>, derived: &ValueRef<T2>)
490    where
491        T1: 'static,
492        T2: 'static,
493    {
494        assert!(
495            Arc::ptr_eq(base.0.as_owner(), derived.0.as_owner()),
496            "ValueRefs should share the same underlying Arc (no cloning)"
497        );
498    }
499
500    #[test]
501    fn test_path_access_no_cloning() {
502        let value = json!({
503            "data": {
504                "nested": {
505                    "deep": "value"
506                }
507            }
508        });
509
510        let root = ValueRef::new(value);
511        let data = root.path("data").unwrap();
512        let nested = data.path("nested").unwrap();
513        let deep = nested.path("deep").unwrap();
514
515        // Verify all path access shares the same root Arc
516        assert_same_base(&root, &data);
517        assert_same_base(&root, &nested);
518        assert_same_base(&root, &deep);
519
520        // Verify the value is correct
521        assert_eq!(deep.as_ref(), &json!("value"));
522    }
523
524    #[test]
525    fn test_array_index_access_no_cloning() {
526        let value = json!([
527            {"name": "first"},
528            {"name": "second"},
529            [1, 2, 3]
530        ]);
531
532        let root = ValueRef::new(value);
533        let first_item = root.index(0).unwrap();
534        let second_item = root.index(1).unwrap();
535        let third_item = root.index(2).unwrap();
536        let nested_array_item = third_item.index(1).unwrap();
537
538        // Verify all array path access shares the same root Arc
539        assert_same_base(&root, &first_item);
540        assert_same_base(&root, &second_item);
541        assert_same_base(&root, &third_item);
542        assert_same_base(&root, &nested_array_item);
543
544        // Verify the values are correct
545        assert_eq!(first_item.as_ref(), &json!({"name": "first"}));
546        assert_eq!(nested_array_item.as_ref(), &json!(2));
547    }
548
549    #[test]
550    fn test_as_object_access_no_cloning() {
551        let value = json!({
552            "metadata": {
553                "count": 42,
554                "items": ["a", "b", "c"]
555            }
556        });
557
558        let root = ValueRef::new(value);
559        let metadata = root.path("metadata").unwrap();
560        let metadata_obj = metadata.as_object().unwrap();
561
562        // Access fields through the object interface
563        let count = metadata_obj.get("count").unwrap();
564        let items = metadata_obj.get("items").unwrap();
565
566        // Verify object casting and field access shares the same root Arc
567        assert_same_base(&root, &metadata);
568        assert_same_base(&root, &metadata_obj);
569        assert_same_base(&root, &count);
570        assert_same_base(&root, &items);
571
572        // Verify the values are correct
573        assert_eq!(count.as_ref(), &json!(42));
574        assert_eq!(items.as_ref(), &json!(["a", "b", "c"]));
575    }
576
577    #[test]
578    fn test_as_array_access_no_cloning() {
579        let value = json!({
580            "items": [
581                {"id": 1, "name": "first"},
582                {"id": 2, "name": "second"},
583                {"id": 3, "name": "third"}
584            ]
585        });
586
587        let root = ValueRef::new(value);
588        let items = root.path("items").unwrap();
589        let items_array = items.as_array().unwrap();
590
591        // Access items through the array interface
592        let first_item = items_array.get(0).unwrap();
593        let second_item = items_array.get(1).unwrap();
594        let third_item = items_array.get(2).unwrap();
595
596        // Access nested field in array item
597        let first_name = first_item.path("name").unwrap();
598
599        // Verify array casting and element access shares the same root Arc
600        assert_same_base(&root, &items);
601        assert_same_base(&root, &items_array);
602        assert_same_base(&root, &first_item);
603        assert_same_base(&root, &second_item);
604        assert_same_base(&root, &third_item);
605        assert_same_base(&root, &first_name);
606
607        // Verify the values are correct
608        assert_eq!(first_item.as_ref(), &json!({"id": 1, "name": "first"}));
609        assert_eq!(first_name.as_ref(), &json!("first"));
610    }
611
612    #[test]
613    fn test_object_iter_no_cloning() {
614        let value = json!({
615            "key1": "value1",
616            "key2": {"nested": "value2"},
617            "key3": [1, 2, 3]
618        });
619
620        let root = ValueRef::new(value);
621        let obj = root.as_object().unwrap();
622
623        // Verify object casting shares the same root Arc
624        assert_same_base(&root, &obj);
625
626        // Iterate over key-value pairs and verify each shares the same root
627        let items = obj.iter();
628        for (_key, value_ref) in items {
629            assert_same_base(&root, &value_ref);
630        }
631    }
632
633    #[test]
634    fn test_array_iter_no_cloning() {
635        let value = json!([
636            {"type": "first"},
637            {"type": "second"},
638            [1, 2, {"nested": "deep"}]
639        ]);
640
641        let root = ValueRef::new(value);
642        let arr = root.as_array().unwrap();
643
644        // Verify array casting shares the same root Arc
645        assert_same_base(&root, &arr);
646
647        // Iterate over array elements and verify each shares the same root
648        let items: Vec<_> = arr.iter().collect();
649        for item in &items {
650            assert_same_base(&root, item);
651        }
652
653        // Access nested content from iterated items
654        let nested_array = &items[2];
655        let nested_arr = nested_array.as_array().unwrap();
656        let deep_object = nested_arr.get(2).unwrap();
657        let deep_value = deep_object.path("nested").unwrap();
658
659        // Verify all nested access still shares the same root
660        assert_same_base(&root, &nested_arr);
661        assert_same_base(&root, &deep_object);
662        assert_same_base(&root, &deep_value);
663
664        // Verify the deep value is correct
665        assert_eq!(deep_value.as_ref(), &json!("deep"));
666    }
667}