Skip to main content

somatize_core/
value.rs

1//! What travels along an edge.
2//!
3//! The price of the core doing the executing: if the engine is in Rust, the data
4//! has to have a shape Rust understands. Five variants are data the core
5//! understands and can compare — no `Json`, which would pull in `serde_json`,
6//! and no shaped `Tensor`, which nobody produces.
7//!
8//! The sixth, [`Value::Opaque`], is of another nature: it carries something the
9//! core **does not look at**. It exists because some values cannot be converted
10//! without being destroyed — a torch tensor mid-autograd-graph round-tripped
11//! through numbers comes back without its `grad_fn`.
12
13use std::any::Any;
14use std::fmt;
15use std::sync::Arc;
16
17/// A datum crossing from one node to the next.
18///
19/// `Arc` wherever the data is heavy, because a value is cloned on every edge
20/// and cloning must not copy.
21#[derive(Clone)]
22pub enum Value {
23    /// Nothing. What a root node receives when you pass it no input.
24    Null,
25    /// A number.
26    Number(f64),
27    /// UTF-8 text.
28    Text(Arc<str>),
29    /// Uninterpreted bytes.
30    Bytes(Arc<Vec<u8>>),
31    /// Several values in order.
32    List(Arc<Vec<Value>>),
33    /// Something the core carries without looking at it, as `dyn Any` so the
34    /// core need not depend on PyO3.
35    ///
36    /// It **only exists in this process and in this run**, and everything else
37    /// follows: no content hash, no serialization, and no comparison but
38    /// identity.
39    Opaque(Arc<dyn Any + Send + Sync>),
40    /// Several named values: what a node with several incoming edges receives,
41    /// keyed by the node that produced each one.
42    ///
43    /// **Ordered**, in the edges' declaration order: a `HashMap` iterates
44    /// differently in each process, so a content hash would be useless.
45    Map(Arc<Vec<(String, Value)>>),
46}
47
48impl Value {
49    /// A number.
50    pub fn number(x: f64) -> Self {
51        Self::Number(x)
52    }
53
54    /// Text.
55    pub fn text(s: impl AsRef<str>) -> Self {
56        Self::Text(Arc::from(s.as_ref()))
57    }
58
59    /// A list.
60    pub fn list(values: impl Into<Vec<Value>>) -> Self {
61        Self::List(Arc::new(values.into()))
62    }
63
64    /// A map, in the order you pass the pairs.
65    pub fn map(pairs: impl Into<Vec<(String, Value)>>) -> Self {
66        Self::Map(Arc::new(pairs.into()))
67    }
68
69    /// The value stored under that key, if this is a map and has it.
70    pub fn get(&self, key: &str) -> Option<&Value> {
71        let Self::Map(pairs) = self else {
72            return None;
73        };
74        pairs.iter().find(|(k, _)| k == key).map(|(_, v)| v)
75    }
76
77    /// A map's values, in order — flattening a map to a list is this.
78    pub fn values(&self) -> Option<Vec<&Value>> {
79        let Self::Map(pairs) = self else {
80            return None;
81        };
82        Some(pairs.iter().map(|(_, v)| v).collect())
83    }
84
85    /// Wraps something so it crosses the graph untouched.
86    pub fn opaque(x: impl Any + Send + Sync) -> Self {
87        Self::Opaque(Arc::new(x))
88    }
89
90    /// What is inside an opaque, if it is of this type.
91    pub fn downcast<T: Any + Send + Sync>(&self) -> Option<&T> {
92        let Self::Opaque(inner) = self else {
93            return None;
94        };
95        inner.downcast_ref::<T>()
96    }
97
98    /// Whether this value, and everything inside it, can leave this process.
99    ///
100    /// `false` for an [`Opaque`](Self::Opaque) at any depth: what it carries
101    /// only exists here. Whoever is about to send it asks first, so the refusal
102    /// names the reason instead of coming out of a serializer.
103    pub fn travels(&self) -> bool {
104        match self {
105            Self::Opaque(_) => false,
106            Self::List(items) => items.iter().all(Self::travels),
107            Self::Map(pairs) => pairs.iter().all(|(_, value)| value.travels()),
108            Self::Null | Self::Number(_) | Self::Text(_) | Self::Bytes(_) => true,
109        }
110    }
111
112    /// What to call this variant in an error message.
113    pub fn type_name(&self) -> &'static str {
114        match self {
115            Self::Null => "null",
116            Self::Number(_) => "number",
117            Self::Text(_) => "text",
118            Self::Bytes(_) => "bytes",
119            Self::List(_) => "list",
120            Self::Map(_) => "map",
121            Self::Opaque(_) => "opaque",
122        }
123    }
124}
125
126impl PartialEq for Value {
127    /// Two opaques are equal only if they are **the same one**: the core cannot
128    /// compare contents it does not look at.
129    fn eq(&self, other: &Self) -> bool {
130        match (self, other) {
131            (Self::Null, Self::Null) => true,
132            (Self::Number(a), Self::Number(b)) => a == b,
133            (Self::Text(a), Self::Text(b)) => a == b,
134            (Self::Bytes(a), Self::Bytes(b)) => a == b,
135            (Self::List(a), Self::List(b)) => a == b,
136            (Self::Map(a), Self::Map(b)) => a == b,
137            (Self::Opaque(a), Self::Opaque(b)) => Arc::ptr_eq(a, b),
138            _ => false,
139        }
140    }
141}
142
143impl fmt::Debug for Value {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        match self {
146            Self::Null => f.write_str("Null"),
147            Self::Number(x) => write!(f, "Number({x})"),
148            Self::Text(s) => write!(f, "Text({s:?})"),
149            Self::Bytes(b) => write!(f, "Bytes({} bytes)", b.len()),
150            Self::List(items) => f.debug_tuple("List").field(items).finish(),
151            Self::Map(pairs) => f.debug_tuple("Map").field(pairs).finish(),
152            Self::Opaque(_) => f.write_str("Opaque(..)"),
153        }
154    }
155}
156
157impl From<&str> for Value {
158    fn from(s: &str) -> Self {
159        Self::text(s)
160    }
161}
162
163impl From<f64> for Value {
164    fn from(x: f64) -> Self {
165        Self::Number(x)
166    }
167}
168
169/// What a value looks like once it leaves this process.
170///
171/// A shadow of [`Value`] and not `Value` itself for one reason worth the fifty
172/// lines: **it has no opaque variant**. That what only exists here cannot be
173/// sent stops being a check somebody has to remember and becomes a type that
174/// cannot be built. The borrowed halves are so that sending copies nothing.
175#[cfg(feature = "serde")]
176mod shadow {
177    use super::Value;
178    use serde::{Deserialize, Deserializer, Serialize, Serializer};
179    use std::borrow::Cow;
180    use std::sync::Arc;
181
182    #[derive(Serialize, Deserialize)]
183    pub(super) enum Shadow<'a> {
184        Null,
185        Number(f64),
186        Text(Cow<'a, str>),
187        Bytes(#[serde(with = "as_bytes")] Cow<'a, [u8]>),
188        List(Vec<Shadow<'a>>),
189        Map(Vec<(Cow<'a, str>, Shadow<'a>)>),
190    }
191
192    /// A byte string, and **not** a list of numbers.
193    ///
194    /// What serde does with a slice by default is a sequence, one element per
195    /// byte: a megabyte of tensor goes on the wire as a million integers, which
196    /// costs both the size and — by far the worse half — a element of work per
197    /// byte at each end. Every format has `serialize_bytes` for exactly this,
198    /// and asking for it by hand is cheaper than a dependency.
199    ///
200    /// Reading takes **both** shapes, so anything written before this still
201    /// opens: a store outlives every binary that wrote into it.
202    mod as_bytes {
203        use super::{Cow, Deserializer, Serializer};
204        use serde::de::{Error, SeqAccess, Visitor};
205        use std::fmt;
206
207        pub fn serialize<S: Serializer>(bytes: &[u8], s: S) -> Result<S::Ok, S::Error> {
208            s.serialize_bytes(bytes)
209        }
210
211        pub fn deserialize<'de, 'a, D: Deserializer<'de>>(d: D) -> Result<Cow<'a, [u8]>, D::Error> {
212            d.deserialize_byte_buf(Bytes).map(Cow::Owned)
213        }
214
215        struct Bytes;
216
217        impl<'de> Visitor<'de> for Bytes {
218            type Value = Vec<u8>;
219
220            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221                f.write_str("bytes")
222            }
223
224            fn visit_bytes<E: Error>(self, bytes: &[u8]) -> Result<Self::Value, E> {
225                Ok(bytes.to_vec())
226            }
227
228            fn visit_byte_buf<E: Error>(self, bytes: Vec<u8>) -> Result<Self::Value, E> {
229                Ok(bytes)
230            }
231
232            /// What was written before bytes were asked for by name.
233            fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
234                let mut bytes = Vec::with_capacity(seq.size_hint().unwrap_or(0));
235                while let Some(byte) = seq.next_element()? {
236                    bytes.push(byte);
237                }
238                Ok(bytes)
239            }
240        }
241    }
242
243    /// The one thing that cannot become a [`Shadow`].
244    pub(super) struct Opaque;
245
246    impl<'a> TryFrom<&'a Value> for Shadow<'a> {
247        type Error = Opaque;
248
249        fn try_from(value: &'a Value) -> Result<Self, Opaque> {
250            Ok(match value {
251                Value::Null => Shadow::Null,
252                Value::Number(x) => Shadow::Number(*x),
253                Value::Text(s) => Shadow::Text(Cow::Borrowed(s)),
254                Value::Bytes(bytes) => Shadow::Bytes(Cow::Borrowed(bytes)),
255                Value::List(items) => Shadow::List(
256                    items
257                        .iter()
258                        .map(Shadow::try_from)
259                        .collect::<Result<_, _>>()?,
260                ),
261                Value::Map(pairs) => Shadow::Map(
262                    pairs
263                        .iter()
264                        .map(|(key, value)| Ok((Cow::Borrowed(key.as_str()), value.try_into()?)))
265                        .collect::<Result<_, Opaque>>()?,
266                ),
267                Value::Opaque(_) => return Err(Opaque),
268            })
269        }
270    }
271
272    impl From<Shadow<'_>> for Value {
273        fn from(shadow: Shadow<'_>) -> Self {
274            match shadow {
275                Shadow::Null => Value::Null,
276                Shadow::Number(x) => Value::Number(x),
277                Shadow::Text(s) => Value::text(s),
278                Shadow::Bytes(bytes) => Value::Bytes(Arc::new(bytes.into_owned())),
279                Shadow::List(items) => {
280                    Value::list(items.into_iter().map(Value::from).collect::<Vec<_>>())
281                }
282                Shadow::Map(pairs) => Value::map(
283                    pairs
284                        .into_iter()
285                        .map(|(key, value)| (key.into_owned(), value.into()))
286                        .collect::<Vec<_>>(),
287                ),
288            }
289        }
290    }
291
292    impl Serialize for Value {
293        /// # Errors
294        /// If anything inside only exists in this process. Ask
295        /// [`travels`](Value::travels) first and the refusal reads better.
296        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
297            Shadow::try_from(self)
298                .map_err(|Opaque| {
299                    serde::ser::Error::custom(
300                        "an opaque value does not leave this process: what it carries \
301                         only exists here",
302                    )
303                })?
304                .serialize(serializer)
305        }
306    }
307
308    impl<'de> Deserialize<'de> for Value {
309        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
310            Shadow::deserialize(deserializer).map(Value::from)
311        }
312    }
313}