Skip to main content

melodium_common/executive/value/
data.rs

1use super::super::Data;
2use super::Value;
3use std::sync::Arc;
4
5/// Trait allowing to get real data based on Rust type.
6///
7/// This trait exist to circumvent E0119 that is disabling us to use TryInto.
8/// See https://github.com/rust-lang/rust/issues/50133
9pub trait GetData<T>: Sized {
10    fn try_data(self) -> Result<T, ()>;
11}
12
13/// Identity extraction: a `Value` handed through unchanged. This is what an unconstrained
14/// mel generic (`#[mel_function] generic T ()`, or any bound not restricted to custom
15/// `Data` types) actually resolves to at the Rust level — the macro type-aliases every
16/// such generic name to `Value` itself — so `Vec<T>`/`Option<T>` involving a bare generic
17/// need `Self: GetData<T>` to hold for `T = Value` in order to compose with the existing
18/// blanket `Vec`/`Option` impls below, exactly like any other scalar type does.
19impl GetData<Value> for Value {
20    fn try_data(self) -> Result<Value, ()> {
21        Ok(self)
22    }
23}
24
25impl From<()> for Value {
26    fn from(value: ()) -> Self {
27        Value::Void(value)
28    }
29}
30
31impl GetData<()> for Value {
32    fn try_data(self) -> Result<(), ()> {
33        match self {
34            Value::Void(_) => Ok(()),
35            _ => Err(()),
36        }
37    }
38}
39
40impl From<i8> for Value {
41    fn from(value: i8) -> Self {
42        Value::I8(value)
43    }
44}
45
46impl GetData<i8> for Value {
47    fn try_data(self) -> Result<i8, ()> {
48        match self {
49            Value::I8(val) => Ok(val),
50            _ => Err(()),
51        }
52    }
53}
54impl From<i16> for Value {
55    fn from(value: i16) -> Self {
56        Value::I16(value)
57    }
58}
59
60impl GetData<i16> for Value {
61    fn try_data(self) -> Result<i16, ()> {
62        match self {
63            Value::I16(val) => Ok(val),
64            _ => Err(()),
65        }
66    }
67}
68impl From<i32> for Value {
69    fn from(value: i32) -> Self {
70        Value::I32(value)
71    }
72}
73
74impl GetData<i32> for Value {
75    fn try_data(self) -> Result<i32, ()> {
76        match self {
77            Value::I32(val) => Ok(val),
78            _ => Err(()),
79        }
80    }
81}
82impl From<i64> for Value {
83    fn from(value: i64) -> Self {
84        Value::I64(value)
85    }
86}
87
88impl GetData<i64> for Value {
89    fn try_data(self) -> Result<i64, ()> {
90        match self {
91            Value::I64(val) => Ok(val),
92            _ => Err(()),
93        }
94    }
95}
96impl From<i128> for Value {
97    fn from(value: i128) -> Self {
98        Value::I128(value)
99    }
100}
101
102impl GetData<i128> for Value {
103    fn try_data(self) -> Result<i128, ()> {
104        match self {
105            Value::I128(val) => Ok(val),
106            _ => Err(()),
107        }
108    }
109}
110
111impl From<u8> for Value {
112    fn from(value: u8) -> Self {
113        Value::U8(value)
114    }
115}
116
117impl GetData<u8> for Value {
118    fn try_data(self) -> Result<u8, ()> {
119        match self {
120            Value::U8(val) => Ok(val),
121            Value::Byte(val) => Ok(val),
122            _ => Err(()),
123        }
124    }
125}
126impl From<u16> for Value {
127    fn from(value: u16) -> Self {
128        Value::U16(value)
129    }
130}
131
132impl GetData<u16> for Value {
133    fn try_data(self) -> Result<u16, ()> {
134        match self {
135            Value::U16(val) => Ok(val),
136            _ => Err(()),
137        }
138    }
139}
140impl From<u32> for Value {
141    fn from(value: u32) -> Self {
142        Value::U32(value)
143    }
144}
145
146impl GetData<u32> for Value {
147    fn try_data(self) -> Result<u32, ()> {
148        match self {
149            Value::U32(val) => Ok(val),
150            _ => Err(()),
151        }
152    }
153}
154impl From<u64> for Value {
155    fn from(value: u64) -> Self {
156        Value::U64(value)
157    }
158}
159
160impl GetData<u64> for Value {
161    fn try_data(self) -> Result<u64, ()> {
162        match self {
163            Value::U64(val) => Ok(val),
164            _ => Err(()),
165        }
166    }
167}
168impl From<u128> for Value {
169    fn from(value: u128) -> Self {
170        Value::U128(value)
171    }
172}
173
174impl GetData<u128> for Value {
175    fn try_data(self) -> Result<u128, ()> {
176        match self {
177            Value::U128(val) => Ok(val),
178            _ => Err(()),
179        }
180    }
181}
182
183impl From<f32> for Value {
184    fn from(value: f32) -> Self {
185        Value::F32(value)
186    }
187}
188
189impl GetData<f32> for Value {
190    fn try_data(self) -> Result<f32, ()> {
191        match self {
192            Value::F32(val) => Ok(val),
193            _ => Err(()),
194        }
195    }
196}
197impl From<f64> for Value {
198    fn from(value: f64) -> Self {
199        Value::F64(value)
200    }
201}
202
203impl GetData<f64> for Value {
204    fn try_data(self) -> Result<f64, ()> {
205        match self {
206            Value::F64(val) => Ok(val),
207            _ => Err(()),
208        }
209    }
210}
211
212impl From<bool> for Value {
213    fn from(value: bool) -> Self {
214        Value::Bool(value)
215    }
216}
217
218impl GetData<bool> for Value {
219    fn try_data(self) -> Result<bool, ()> {
220        match self {
221            Value::Bool(val) => Ok(val),
222            _ => Err(()),
223        }
224    }
225}
226impl From<char> for Value {
227    fn from(value: char) -> Self {
228        Value::Char(value)
229    }
230}
231
232impl GetData<char> for Value {
233    fn try_data(self) -> Result<char, ()> {
234        match self {
235            Value::Char(val) => Ok(val),
236            _ => Err(()),
237        }
238    }
239}
240impl From<String> for Value {
241    fn from(value: String) -> Self {
242        Value::String(value)
243    }
244}
245
246impl GetData<String> for Value {
247    fn try_data(self) -> Result<String, ()> {
248        match self {
249            Value::String(val) => Ok(val),
250            _ => Err(()),
251        }
252    }
253}
254
255impl<T: Into<Value>> From<Option<T>> for Value {
256    fn from(value: Option<T>) -> Self {
257        Value::Option(value.map(|val| Box::new(val.into())))
258    }
259}
260
261impl<T> GetData<Option<T>> for Value
262where
263    Self: GetData<T>,
264{
265    fn try_data(self) -> Result<Option<T>, ()> {
266        match self {
267            Value::Option(val) => {
268                if let Some(val) = val {
269                    match val.try_data() {
270                        Ok(val) => Ok(Some(val)),
271                        Err(_) => Err(()),
272                    }
273                } else {
274                    Ok(None)
275                }
276            }
277            _ => Err(()),
278        }
279    }
280}
281
282impl<T: Into<Value> + 'static> From<Vec<T>> for Value {
283    fn from(value: Vec<T>) -> Self {
284        // `try_from_vec` recognizes `T` at runtime (via `Any`) as one of `PackedArray`'s
285        // primitive types and, if so, packs it — automatically, for every caller that
286        // uses this conversion (`.into()`), with no per-call-site opt-in needed. Falls
287        // through to the ordinary boxed representation for anything else (`String`,
288        // `Arc<dyn Data>`, nested `Vec`/`Option`, ...). See ticket #116.
289        match super::PackedArray::try_from_vec(value) {
290            Ok(packed) => Value::Packed(packed),
291            Err(value) => Value::Vec(value.into_iter().map(|val| val.into()).collect()),
292        }
293    }
294}
295
296impl<T: 'static> GetData<Vec<T>> for Value
297where
298    Self: GetData<T>,
299{
300    fn try_data(self) -> Result<Vec<T>, ()> {
301        match self {
302            Value::Vec(val) => {
303                let mut result = Vec::with_capacity(val.len());
304                for val in val {
305                    match val.try_data() {
306                        Ok(val) => result.push(val),
307                        Err(_) => return Err(()),
308                    }
309                }
310                Ok(result)
311            }
312            // Direct extraction for a `Value::Packed`: `try_into_vec` recognizes `T` at
313            // runtime (via `Any`) as the array's actual element type and, if so, hands
314            // back the `Vec<T>` straight from the array's storage — no intermediate
315            // `Value` ever created. This covers the common case (`T` one of
316            // `PackedArray`'s own primitive types) with zero boxing; callers that can
317            // accept `Arc<Vec<T>>` instead should extract through
318            // `GetData<Arc<Vec<T>>>` (see `packed.rs`), which stays zero-copy even when
319            // the array is shared.
320            //
321            // If `T` doesn't match the array's stored primitive directly (e.g. `T =
322            // Value` itself — what a bare, unconstrained mel generic resolves to, see
323            // `GetData<Value> for Value` above), that's not necessarily a genuine type
324            // mismatch: falling back to the same expand-then-extract path `Value::Vec`
325            // uses above is still correct, just not the zero-copy fast path. Skipping
326            // this fallback would make `GetData<Vec<T>>` reject a legitimately-typed
327            // `Packed` source whenever `T` isn't one of the 15 packable primitives -
328            // exactly the panic/silent-failure bug this whole mechanism exists to avoid.
329            Value::Packed(arr) => match arr.try_into_vec() {
330                Ok(vec) => Ok(vec),
331                Err(arr) => {
332                    let val = arr.into_values();
333                    let mut result = Vec::with_capacity(val.len());
334                    for val in val {
335                        match val.try_data() {
336                            Ok(val) => result.push(val),
337                            Err(_) => return Err(()),
338                        }
339                    }
340                    Ok(result)
341                }
342            },
343            _ => Err(()),
344        }
345    }
346}
347
348impl From<Arc<dyn Data>> for Value {
349    fn from(value: Arc<dyn Data>) -> Self {
350        Value::Data(value)
351    }
352}
353
354impl GetData<Arc<dyn Data>> for Value {
355    fn try_data(self) -> Result<Arc<dyn Data>, ()> {
356        match self {
357            Value::Data(val) => Ok(val),
358            _ => Err(()),
359        }
360    }
361}
362
363/// Casts straight to a concrete `Data` implementor, folding the two-step
364/// `GetData::<Arc<dyn Data>>::try_data(val).unwrap().downcast_arc::<D>().unwrap()` idiom
365/// (used throughout `libs/*-mel` for every custom data type) into one call — and, via
366/// `recv_one_as`, into one non-panicking `RecvResult`. Coexists with the `Arc<dyn Data>`
367/// impl above without conflict: `D` carries an implicit `Sized` bound here, which the
368/// unsized `dyn Data` can never satisfy, so the two can never overlap for the same type.
369impl<D: Data> GetData<Arc<D>> for Value {
370    fn try_data(self) -> Result<Arc<D>, ()> {
371        let data: Arc<dyn Data> = GetData::<Arc<dyn Data>>::try_data(self)?;
372        data.downcast_arc::<D>().map_err(|_| ())
373    }
374}
375
376#[cfg(test)]
377mod arc_data_getdata_tests {
378    use super::*;
379
380    // Type-check only: does the recursive Vec<T>/Option<T> machinery compose with the new
381    // Arc<D: Data> impl for free, without any additional impl written for these shapes?
382    fn _assert_composes<D: Data>()
383    where
384        Value: GetData<Vec<Arc<D>>> + GetData<Option<Arc<D>>> + GetData<Vec<Option<Arc<D>>>>,
385    {
386    }
387}
388
389#[cfg(test)]
390mod auto_packing_tests {
391    use super::*;
392    use crate::executive::PackedArray;
393
394    // The whole point of routing `From<Vec<T>>` through `PackedArray::try_from_vec`:
395    // an ordinary `.into()` call, exactly what every existing and third-party caller
396    // already writes, must produce `Value::Packed` automatically for a packable
397    // primitive - no call site needs to know `Packed` exists.
398    #[test]
399    fn into_produces_packed_for_a_packable_primitive() {
400        let value: Value = vec![1u8, 2, 3].into();
401        assert!(matches!(value, Value::Packed(PackedArray::U8(_))));
402    }
403
404    #[test]
405    fn into_still_produces_the_boxed_form_for_a_non_packable_type() {
406        let value: Value = vec!["a".to_string(), "b".to_string()].into();
407        assert!(matches!(value, Value::Vec(_)));
408    }
409
410    // `Value` extracting to itself, trivially - what a bare mel generic (`generic T ()`)
411    // actually resolves to at the Rust level, per `melodium-macro`'s typedef codegen.
412    #[test]
413    fn value_extracts_to_itself() {
414        let value = Value::U64(42);
415        let extracted: Value = value.clone().try_data().unwrap();
416        assert_eq!(extracted, value);
417    }
418
419    // The exact bug this fallback exists to close: a `#[mel_function]` with an
420    // unconstrained generic `Vec<T>` parameter (e.g. `contains(vector: Vec<T>, ...)`)
421    // extracts via `GetData::<Vec<Value>>::try_data`, since `T` is type-aliased to
422    // `Value` for a bare generic. Before the fallback, `PackedArray::try_into_vec::<Value>`
423    // would always fail (no primitive is ever `Value` itself) and the whole extraction
424    // would incorrectly reject a legitimately `Value::Packed` source.
425    #[test]
426    fn vec_of_value_extracts_correctly_from_a_packed_source() {
427        let value = Value::Packed(PackedArray::I64(Arc::new(vec![1, 2, 3])));
428        let extracted: Vec<Value> = value.try_data().unwrap();
429        assert_eq!(extracted, vec![Value::I64(1), Value::I64(2), Value::I64(3)]);
430    }
431
432    #[test]
433    fn vec_of_value_still_extracts_correctly_from_a_boxed_source() {
434        let value = Value::Vec(vec![Value::I64(1), Value::I64(2)]);
435        let extracted: Vec<Value> = value.try_data().unwrap();
436        assert_eq!(extracted, vec![Value::I64(1), Value::I64(2)]);
437    }
438
439    // `From<Option<T>>` composes through `.into()` too, so this must auto-pack exactly
440    // like the bare `Vec<u8>` case, with no extra code needed for the nested shape.
441    #[test]
442    fn into_auto_packs_through_nested_option() {
443        let value: Value = Some(vec![1u8, 2, 3]).into();
444        match value {
445            Value::Option(Some(inner)) => {
446                assert!(matches!(*inner, Value::Packed(PackedArray::U8(_))));
447            }
448            other => panic!(
449                "expected Value::Option(Some(Value::Packed(_))), got {:?}",
450                other
451            ),
452        }
453    }
454
455    // Round-trips: what auto-packing constructs, the existing extraction path (whether
456    // the fast `Arc<Vec<T>>` route or the generic `Vec<T>` route) must read back.
457    #[test]
458    fn auto_packed_value_extracts_correctly_both_ways() {
459        let value: Value = vec![1u8, 2, 3].into();
460
461        let as_vec: Vec<u8> = value.clone().try_data().unwrap();
462        assert_eq!(as_vec, vec![1, 2, 3]);
463
464        let as_arc: Arc<Vec<u8>> = value.try_data().unwrap();
465        assert_eq!(*as_arc, vec![1, 2, 3]);
466    }
467}