Skip to main content

nanvm_lib/vm/impls/
try_from.rs

1//! One `TryFrom<Any<A>> for X` per payload type, the inverse of each
2//! `From<X> for Unpacked<A>` in `from.rs`; why both stay hand-written is
3//! there.
4
5use crate::{
6    common::sized_index::SizedIndex,
7    vm::{
8        Any, Array, BigInt, Function, IVm, Number, Object, String, Unpacked, error,
9        nullish::Nullish,
10    },
11};
12
13impl<A: IVm> TryFrom<Any<A>> for Nullish {
14    type Error = Any<A>;
15    fn try_from(value: Any<A>) -> Result<Self, Self::Error> {
16        let Unpacked::Nullish(result) = value.into() else {
17            return Err(error::unexpected_type());
18        };
19        Ok(result)
20    }
21}
22
23impl<A: IVm> TryFrom<Any<A>> for bool {
24    type Error = Any<A>;
25    fn try_from(value: Any<A>) -> Result<Self, Self::Error> {
26        let Unpacked::Boolean(result) = value.into() else {
27            return Err(error::unexpected_type());
28        };
29        Ok(result)
30    }
31}
32
33impl<A: IVm> TryFrom<Any<A>> for Number {
34    type Error = Any<A>;
35    fn try_from(value: Any<A>) -> Result<Self, Self::Error> {
36        let Unpacked::Number(result) = value.into() else {
37            return Err(error::unexpected_type());
38        };
39        Ok(result)
40    }
41}
42
43impl<A: IVm> TryFrom<Any<A>> for Array<A> {
44    type Error = Any<A>;
45    fn try_from(value: Any<A>) -> Result<Self, Self::Error> {
46        let Unpacked::Array(result) = value.into() else {
47            return Err(error::unexpected_type());
48        };
49        Ok(result)
50    }
51}
52
53/// An array of exactly `N` items as a fixed-size Rust array, to destructure:
54/// `let [tag, value]: [Any<A>; 2] = array.try_into()?`. An array of any other
55/// length is handed back unchanged, as `Vec`'s conversion hands back the `Vec`.
56impl<A: IVm, const N: usize> TryFrom<Array<A>> for [Any<A>; N] {
57    type Error = Array<A>;
58    fn try_from(value: Array<A>) -> Result<Self, Self::Error> {
59        if value.length() as usize != N {
60            return Err(value);
61        }
62        Ok(core::array::from_fn(|i| value[i as u32].clone()))
63    }
64}
65
66impl<A: IVm> TryFrom<Any<A>> for BigInt<A> {
67    type Error = Any<A>;
68    fn try_from(value: Any<A>) -> Result<Self, Self::Error> {
69        let Unpacked::BigInt(result) = value.into() else {
70            return Err(error::unexpected_type());
71        };
72        Ok(result)
73    }
74}
75
76impl<A: IVm> TryFrom<Any<A>> for Function<A> {
77    type Error = Any<A>;
78    fn try_from(value: Any<A>) -> Result<Self, Self::Error> {
79        let Unpacked::Function(result) = value.into() else {
80            return Err(error::unexpected_type());
81        };
82        Ok(result)
83    }
84}
85
86impl<A: IVm> TryFrom<Any<A>> for Object<A> {
87    type Error = Any<A>;
88    fn try_from(value: Any<A>) -> Result<Self, Self::Error> {
89        let Unpacked::Object(result) = value.into() else {
90            return Err(error::unexpected_type());
91        };
92        Ok(result)
93    }
94}
95
96impl<A: IVm> TryFrom<Any<A>> for String<A> {
97    type Error = Any<A>;
98    fn try_from(value: Any<A>) -> Result<Self, Self::Error> {
99        let Unpacked::String(result) = value.into() else {
100            return Err(error::unexpected_type());
101        };
102        Ok(result)
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use crate::{
109        naive::Naive,
110        vm::{Any, Array, ToAny, ToArray},
111    };
112
113    type A = Naive;
114
115    fn array<const N: usize>(items: [f64; N]) -> Array<A> {
116        items.map(|n| n.to_any::<A>()).to_array()
117    }
118
119    #[test]
120    fn an_array_of_exactly_n_items_is_unpacked() {
121        let [a, b]: [Any<A>; 2] = array([1.0, 2.0]).try_into().unwrap();
122        assert_eq!((a, b), (1.0.to_any(), 2.0.to_any()));
123        let []: [Any<A>; 0] = array([]).try_into().unwrap();
124    }
125
126    #[test]
127    fn any_other_length_is_handed_back() {
128        for items in [array([1.0]), array([1.0, 2.0, 3.0])] {
129            let refused: Result<[Any<A>; 2], _> = items.clone().try_into();
130            assert_eq!(refused, Err(items));
131        }
132    }
133}