Skip to main content

somatize_core/
value.rs

1//! Typed values flowing between filters in a pipeline.
2//!
3//! [`Value`] variants: Tensor (f64 array with shape), JSON, Bytes, Empty.
4//! Values are serializable and content-addressable via [`CacheKey`].
5
6use serde::{Deserialize, Serialize};
7use std::fmt;
8use std::sync::Arc;
9
10/// Typed values flowing between filters in a pipeline.
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
12#[serde(tag = "type", content = "data")]
13#[non_exhaustive]
14pub enum Value {
15    /// Numeric tensor data (shape + flat data).
16    /// `values` is wrapped in [`Arc`] so that cloning a `Value` is O(1).
17    Tensor {
18        values: Arc<Vec<f64>>,
19        shape: Vec<usize>,
20    },
21
22    /// Structured JSON data (Arc-wrapped for cheap cloning).
23    Json(Arc<serde_json::Value>),
24
25    /// Raw bytes (Arc-wrapped for cheap cloning).
26    Bytes(Arc<Vec<u8>>),
27
28    /// Empty / void value
29    Empty,
30}
31
32impl Value {
33    pub fn tensor(values: Vec<f64>, shape: Vec<usize>) -> Self {
34        Self::Tensor {
35            values: Arc::new(values),
36            shape,
37        }
38    }
39
40    pub fn json(val: serde_json::Value) -> Self {
41        Self::Json(Arc::new(val))
42    }
43
44    pub fn bytes(data: Vec<u8>) -> Self {
45        Self::Bytes(Arc::new(data))
46    }
47
48    pub fn is_empty(&self) -> bool {
49        matches!(self, Self::Empty)
50    }
51
52    /// Try to extract tensor data.
53    pub fn as_tensor(&self) -> Option<(&[f64], &[usize])> {
54        match self {
55            Self::Tensor { values, shape } => Some((values, shape)),
56            _ => None,
57        }
58    }
59
60    /// Try to extract JSON value.
61    pub fn as_json(&self) -> Option<&serde_json::Value> {
62        match self {
63            Self::Json(v) => Some(v),
64            _ => None,
65        }
66    }
67
68    /// Number of elements (for tensors) or bytes.
69    pub fn size(&self) -> usize {
70        match self {
71            Self::Tensor { values, .. } => values.len(),
72            Self::Json(v) => v.to_string().len(),
73            Self::Bytes(b) => b.len(),
74            Self::Empty => 0,
75        }
76    }
77}
78
79impl fmt::Display for Value {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        match self {
82            Self::Tensor { shape, values } => {
83                write!(f, "Tensor(shape={shape:?}, len={})", values.len())
84            }
85            Self::Json(v) => write!(f, "Json({v})"),
86            Self::Bytes(b) => write!(f, "Bytes(len={})", b.len()),
87            Self::Empty => write!(f, "Empty"),
88        }
89    }
90}
91
92impl From<Vec<f64>> for Value {
93    fn from(values: Vec<f64>) -> Self {
94        let len = values.len();
95        Self::Tensor {
96            values: Arc::new(values),
97            shape: vec![len],
98        }
99    }
100}
101
102impl From<serde_json::Value> for Value {
103    fn from(v: serde_json::Value) -> Self {
104        Self::Json(Arc::new(v))
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111    use serde_json::json;
112
113    #[test]
114    fn tensor_creation_and_access() {
115        let v = Value::tensor(vec![1.0, 2.0, 3.0, 4.0], vec![2, 2]);
116        let (data, shape) = v.as_tensor().unwrap();
117        assert_eq!(data, &[1.0, 2.0, 3.0, 4.0]);
118        assert_eq!(shape, &[2, 2]);
119    }
120
121    #[test]
122    fn json_value() {
123        let v = Value::json(json!({"key": "value"}));
124        let j = v.as_json().unwrap();
125        assert_eq!(j["key"], "value");
126    }
127
128    #[test]
129    fn empty_value() {
130        let v = Value::Empty;
131        assert!(v.is_empty());
132        assert_eq!(v.size(), 0);
133    }
134
135    #[test]
136    fn from_vec_f64() {
137        let v: Value = vec![1.0, 2.0, 3.0].into();
138        let (data, shape) = v.as_tensor().unwrap();
139        assert_eq!(data, &[1.0, 2.0, 3.0]);
140        assert_eq!(shape, &[3]);
141    }
142
143    #[test]
144    fn display_formatting() {
145        let t = Value::tensor(vec![1.0, 2.0], vec![2]);
146        assert_eq!(t.to_string(), "Tensor(shape=[2], len=2)");
147
148        let e = Value::Empty;
149        assert_eq!(e.to_string(), "Empty");
150    }
151
152    #[test]
153    fn serde_roundtrip() {
154        let values = vec![
155            Value::tensor(vec![1.0, 2.0, 3.0], vec![3]),
156            Value::json(json!({"a": 1})),
157            Value::bytes(vec![0xDE, 0xAD]),
158            Value::Empty,
159        ];
160
161        for v in values {
162            let serialized = serde_json::to_string(&v).unwrap();
163            let deserialized: Value = serde_json::from_str(&serialized).unwrap();
164            assert_eq!(v, deserialized);
165        }
166    }
167
168    #[test]
169    fn size_returns_correct_values() {
170        assert_eq!(Value::tensor(vec![1.0; 100], vec![10, 10]).size(), 100);
171        assert_eq!(Value::bytes(vec![0; 50]).size(), 50);
172        assert!(Value::json(json!({"key": "val"})).size() > 0);
173    }
174}