Skip to main content

steel/
conversions.rs

1use crate::values::lists::List;
2
3use crate::{
4    gc::Gc,
5    rerrs::ErrorKind,
6    rvals::{AsRefSteelValFromUnsized, FromSteelVal, IntoSteelVal, Result},
7    SteelErr, SteelVal,
8};
9use std::{
10    borrow::Cow,
11    collections::{HashMap, HashSet},
12};
13
14use crate::values::HashMap as ImmutableHashMap;
15use crate::values::HashSet as ImmutableHashSet;
16
17#[cfg(feature = "anyhow")]
18mod anyhow_conversion {
19    use crate::{rvals::IntoSteelVal, SteelVal};
20
21    impl IntoSteelVal for anyhow::Error {
22        fn into_steelval(self) -> crate::rvals::Result<crate::SteelVal> {
23            Ok(SteelVal::StringV(format!("{:#?}", self).into()))
24        }
25    }
26}
27
28impl IntoSteelVal for SteelVal {
29    fn into_steelval(self) -> Result<SteelVal> {
30        Ok(self)
31    }
32}
33
34// impl IntoSteelVal for Result<SteelVal> {
35//     fn into_steelval(self) -> Result<SteelVal> {
36//         self
37//     }
38// }
39
40impl FromSteelVal for Gc<ImmutableHashMap<SteelVal, SteelVal>> {
41    fn from_steelval(val: &SteelVal) -> Result<Self> {
42        if let SteelVal::HashMapV(hm) = val {
43            Ok(hm.0.clone())
44        } else {
45            stop!(TypeMismatch => "Unable to convert Steelval to HashMap, found: {}", val);
46        }
47    }
48}
49
50// impl IntoSteelVal for str {
51//     fn into_steelval(self) -> Result<SteelVal> {
52//         Ok(SteelVal::StringV(self.to_string().into()))
53//     }
54// }
55
56// impl<T: IntoSteelVal + Clone> IntoSteelVal for Cow<'_, T> {
57//     fn into_steelval(self) -> Result<SteelVal> {
58//         match self {
59//             Cow::Borrowed(b) => b.clone().into_steelval(),
60//             Cow::Owned(o) => o.into_steelval(),
61//         }
62//     }
63// }
64
65impl IntoSteelVal for Cow<'_, str> {
66    fn into_steelval(self) -> Result<SteelVal> {
67        match self {
68            Cow::Borrowed(b) => b.into_steelval(),
69            Cow::Owned(o) => o.into_steelval(),
70        }
71    }
72}
73
74impl FromSteelVal for Cow<'_, str> {
75    fn from_steelval(val: &SteelVal) -> Result<Self> {
76        if let SteelVal::StringV(s) = val {
77            Ok(Cow::Owned(s.to_string()))
78        } else {
79            stop!(TypeMismatch => "expected string, found {:?}", val)
80        }
81    }
82}
83
84impl<T: IntoSteelVal + Clone> IntoSteelVal for &[T] {
85    fn into_steelval(self) -> Result<SteelVal> {
86        self.iter()
87            .map(|x| x.clone().into_steelval())
88            .collect::<Result<List<_>>>()
89            .map(SteelVal::ListV)
90    }
91}
92
93// TODO: @Matt - figure out how to get this to actually return the right value. At the moment,
94// it seems I can't get this to return the correct value
95impl<T: FromSteelVal + Clone> AsRefSteelValFromUnsized<T> for T {
96    type Output = Vec<T>;
97
98    fn as_ref_from_unsized(val: &SteelVal) -> Result<Self::Output> {
99        if let SteelVal::ListV(v) = val {
100            v.iter()
101                .map(|x| T::from_steelval(x))
102                .collect::<Result<Vec<_>>>()
103        } else {
104            stop!(TypeMismatch => "expected list, found: {:?}", val);
105        }
106    }
107}
108
109impl<A: IntoSteelVal, B: IntoSteelVal> IntoSteelVal for (A, B) {
110    fn into_steelval(self) -> Result<SteelVal> {
111        Ok(SteelVal::ListV(
112            vec![self.0.into_steelval()?, self.1.into_steelval()?].into(),
113        ))
114    }
115}
116
117// Vectors should translate into vectors in rust
118impl<T: IntoSteelVal> IntoSteelVal for Vec<T> {
119    fn into_steelval(self) -> Result<SteelVal> {
120        let vec_vals: Result<Vec<SteelVal>> = self.into_iter().map(|x| x.into_steelval()).collect();
121
122        match vec_vals {
123            Ok(l) => Ok(SteelVal::ListV(l.into())),
124            _ => Err(SteelErr::new(
125                ErrorKind::ConversionError,
126                "Could not convert vector of values to SteelVal list".to_string(),
127            )),
128        }
129    }
130}
131
132impl<T: FromSteelVal> FromSteelVal for Vec<T> {
133    fn from_steelval(val: &SteelVal) -> Result<Self> {
134        match val {
135            SteelVal::ListV(l) => {
136                let result_vec_vals: Result<Self> = l
137                    .into_iter()
138                    .map(|x| FromSteelVal::from_steelval(x))
139                    .collect();
140
141                match result_vec_vals {
142                    Ok(x) => Ok(x),
143                    _ => Err(SteelErr::new(
144                        ErrorKind::ConversionError,
145                        "Could not convert SteelVal list to Vector of values".to_string(),
146                    )),
147                }
148            }
149            // SteelVal::Pair(_) => {
150            //     let result_vec_vals: Result<Self> = SteelVal::iter(val.clone())
151            //         .into_iter()
152            //         .map(FromSteelVal::from_steelval)
153            //         .collect();
154
155            //     match result_vec_vals {
156            //         Ok(x) => Ok(x),
157            //         _ => Err(SteelErr::new(
158            //             ErrorKind::ConversionError,
159            //             "Could not convert SteelVal list to Vector of values".to_string(),
160            //         )),
161            //     }
162            // }
163            SteelVal::VectorV(v) => {
164                let result_vec_vals: Result<Self> =
165                    v.iter().map(FromSteelVal::from_steelval).collect();
166                match result_vec_vals {
167                    Ok(x) => Ok(x),
168                    _ => Err(SteelErr::new(
169                        ErrorKind::ConversionError,
170                        "Could not convert SteelVal list to Vector of values".to_string(),
171                    )),
172                }
173            } // TODO
174            _ => Err(SteelErr::new(
175                ErrorKind::ConversionError,
176                "Could not convert SteelVal list to Vector of values".to_string(),
177            )),
178        }
179    }
180}
181
182impl<T: FromSteelVal> FromSteelVal for Box<[T]> {
183    fn from_steelval(val: &SteelVal) -> Result<Self> {
184        match val {
185            SteelVal::ListV(l) => {
186                let result_vec_vals: Result<Self> = l
187                    .into_iter()
188                    .map(|x| FromSteelVal::from_steelval(x))
189                    .collect();
190
191                match result_vec_vals {
192                    Ok(x) => Ok(x),
193                    _ => Err(SteelErr::new(
194                        ErrorKind::ConversionError,
195                        "Could not convert SteelVal list to Vector of values".to_string(),
196                    )),
197                }
198            }
199            SteelVal::VectorV(v) => {
200                let result_vec_vals: Result<Self> =
201                    v.iter().map(FromSteelVal::from_steelval).collect();
202                match result_vec_vals {
203                    Ok(x) => Ok(x),
204                    _ => Err(SteelErr::new(
205                        ErrorKind::ConversionError,
206                        "Could not convert SteelVal list to Vector of values".to_string(),
207                    )),
208                }
209            } // TODO
210            _ => Err(SteelErr::new(
211                ErrorKind::ConversionError,
212                "Could not convert SteelVal list to Vector of values".to_string(),
213            )),
214        }
215    }
216}
217
218impl FromSteelVal for Box<str> {
219    fn from_steelval(val: &SteelVal) -> Result<Self> {
220        if let SteelVal::StringV(s) = val {
221            Ok(s.as_str().into())
222        } else {
223            stop!(TypeMismatch => "Unable to convert {} into Box<str>", val);
224        }
225    }
226}
227
228// HashMap
229impl<K: IntoSteelVal, V: IntoSteelVal> IntoSteelVal for HashMap<K, V> {
230    fn into_steelval(mut self) -> Result<SteelVal> {
231        let mut hm = ImmutableHashMap::new();
232        for (key, val) in self.drain() {
233            hm.insert(key.into_steelval()?, val.into_steelval()?);
234        }
235        Ok(SteelVal::HashMapV(Gc::new(hm).into()))
236    }
237}
238
239impl<K: FromSteelVal + Eq + core::hash::Hash, V: FromSteelVal> FromSteelVal for HashMap<K, V> {
240    fn from_steelval(val: &SteelVal) -> Result<Self> {
241        // todo!()
242        if let SteelVal::HashMapV(hm) = val {
243            let mut h = HashMap::new();
244            for (key, value) in hm.0.unwrap().into_iter() {
245                h.insert(K::from_steelval(&key)?, V::from_steelval(&value)?);
246            }
247            Ok(h)
248        } else {
249            Err(SteelErr::new(
250                ErrorKind::ConversionError,
251                "Could not convert SteelVal to HashMap".to_string(),
252            ))
253        }
254    }
255}
256
257// BTreeMap
258// impl<K: IntoSteelVal, V: IntoSteelVal> IntoSteelVal for BTreeMap<K, V> {
259//     fn into_steelval(self) -> Result<SteelVal> {
260//         let mut hm = im_rc::HashMap::new();
261//         for (key, val) in self.drain() {
262//             hm.insert(key.into_steelval()?, val.into_steelval()?);
263//         }
264//         Ok(SteelVal::HashMapV(Gc::new(hm)))
265//     }
266// }
267
268// impl<K: FromSteelVal, V: FromSteelVal> FromSteelVal for BTreeMap<K, V> {
269//     fn from_steelval(val: SteelVal) -> Result<Self> {
270//         todo!()
271//     }
272// }
273
274impl<A: FromSteelVal, B: FromSteelVal> FromSteelVal for (A, B) {
275    fn from_steelval(val: &SteelVal) -> Result<Self> {
276        if let SteelVal::ListV(l) = val {
277            if l.len() != 2 {
278                return Err(SteelErr::new(ErrorKind::ConversionError, format!("Could not convert steelval to (A, B): {:?} - list was not length of 2, found length: {}", val, l.len())));
279            }
280
281            Ok((
282                A::from_steelval(l.get(0).unwrap())?,
283                B::from_steelval(l.get(1).unwrap())?,
284            ))
285        } else {
286            Err(SteelErr::new(
287                ErrorKind::ConversionError,
288                format!("Could not convert SteelVal to (A, B): {:?}", val),
289            ))
290        }
291    }
292}
293
294// HashSet
295impl<K: IntoSteelVal> IntoSteelVal for HashSet<K> {
296    fn into_steelval(mut self) -> Result<SteelVal> {
297        let mut hs = ImmutableHashSet::new();
298        for value in self.drain() {
299            hs.insert(value.into_steelval()?);
300        }
301        Ok(SteelVal::HashSetV(Gc::new(hs).into()))
302    }
303}
304
305impl<K: FromSteelVal + Eq + core::hash::Hash> FromSteelVal for HashSet<K> {
306    fn from_steelval(val: &SteelVal) -> Result<Self> {
307        if let SteelVal::HashSetV(hs) = val {
308            let mut h = HashSet::new();
309            for k in hs.0.unwrap().into_iter() {
310                h.insert(K::from_steelval(&k)?);
311            }
312            Ok(h)
313        } else {
314            Err(SteelErr::new(
315                ErrorKind::ConversionError,
316                "Could not convert SteelVal to HashSet".to_string(),
317            ))
318        }
319    }
320}
321
322// BTreeSet
323// impl<K: IntoSteelVal> IntoSteelVal for BTreeSet<K> {
324//     fn into_steelval(self) -> Result<SteelVal> {
325//         todo!()
326//     }
327// }
328
329// impl<K: FromSteelVal> FromSteelVal for BTreeSet<K> {
330//     fn from_steelval(val: SteelVal) -> Result<Self> {
331//         todo!()
332//     }
333// }
334
335#[cfg(test)]
336mod conversion_tests {
337
338    use super::*;
339
340    #[cfg(not(feature = "sync"))]
341    use im_rc::vector;
342
343    #[cfg(not(feature = "sync"))]
344    use im_rc::hashmap;
345
346    #[cfg(not(feature = "sync"))]
347    use im_rc::hashset;
348
349    #[cfg(all(feature = "sync", not(feature = "imbl")))]
350    use im::vector;
351
352    #[cfg(all(feature = "sync", not(feature = "imbl")))]
353    use im::hashmap;
354
355    #[cfg(all(feature = "sync", not(feature = "imbl")))]
356    use im::hashset;
357
358    #[cfg(all(feature = "sync", feature = "imbl"))]
359    use steel_imbl::generic_vector as vector;
360
361    #[cfg(all(feature = "sync", feature = "imbl"))]
362    use steel_imbl::generic_hashmap as hashmap;
363
364    #[cfg(all(feature = "sync", feature = "imbl"))]
365    use steel_imbl::generic_hashset as hashset;
366
367    #[test]
368    fn vec_into_list() {
369        let input_vec = vec![1, 2];
370        // let expected = SteelVal::Pair(Gc::new(ConsCell::new(
371        //     SteelVal::IntV(1),
372        //     Some(Gc::new(ConsCell::new(SteelVal::IntV(2), None))),
373        // )));
374
375        let expected = SteelVal::ListV(vec![SteelVal::IntV(1), SteelVal::IntV(2)].into());
376
377        assert_eq!(input_vec.into_steelval().unwrap(), expected)
378    }
379
380    #[test]
381    fn vec_from_list() {
382        let input_list = SteelVal::ListV(vec![SteelVal::IntV(1), SteelVal::IntV(2)].into());
383
384        let expected = vec![1, 2];
385        let result = <Vec<i32>>::from_steelval(&input_list).unwrap();
386
387        assert_eq!(result, expected)
388    }
389
390    #[test]
391    fn vec_from_vector() {
392        let input_vector = vector![SteelVal::IntV(1), SteelVal::IntV(2)].into();
393
394        let expected = vec![1, 2];
395        let result = <Vec<i32>>::from_steelval(&input_vector).unwrap();
396        assert_eq!(result, expected);
397    }
398
399    #[test]
400    fn vec_from_steelval_error() {
401        let input = SteelVal::IntV(2);
402        assert!(<Vec<i32>>::from_steelval(&input).is_err());
403    }
404
405    #[test]
406    fn hashmap_into_steelval() {
407        let mut input = HashMap::new();
408        input.insert("foo".to_string(), "bar".to_string());
409        input.insert("foo2".to_string(), "bar2".to_string());
410
411        let expected = SteelVal::HashMapV(
412            Gc::new(hashmap! {
413                SteelVal::StringV("foo".into()) => SteelVal::StringV("bar".into()),
414                SteelVal::StringV("foo2".into()) => SteelVal::StringV("bar2".into())
415            })
416            .into(),
417        );
418
419        assert_eq!(input.into_steelval().unwrap(), expected);
420    }
421
422    #[test]
423    fn hashmap_from_steelval_hashmap() {
424        let input = SteelVal::HashMapV(
425            Gc::new(hashmap! {
426                SteelVal::StringV("foo".into()) => SteelVal::StringV("bar".into()),
427                SteelVal::StringV("foo2".into()) => SteelVal::StringV("bar2".into())
428            })
429            .into(),
430        );
431
432        let mut expected = HashMap::new();
433        expected.insert("foo".to_string(), "bar".to_string());
434        expected.insert("foo2".to_string(), "bar2".to_string());
435
436        assert_eq!(
437            <HashMap<String, String>>::from_steelval(&input).unwrap(),
438            expected
439        );
440    }
441
442    #[test]
443    fn hashset_into_steelval() {
444        let mut input = HashSet::new();
445        input.insert("foo".to_string());
446        input.insert("bar".to_string());
447
448        let expected = SteelVal::HashSetV(
449            Gc::new(hashset! {
450                SteelVal::StringV("foo".into()),
451                SteelVal::StringV("bar".into())
452            })
453            .into(),
454        );
455
456        assert_eq!(input.into_steelval().unwrap(), expected);
457    }
458
459    #[test]
460    fn hashset_from_steelval_hashset() {
461        let input = SteelVal::HashSetV(
462            Gc::new(hashset! {
463                SteelVal::StringV("foo".into()),
464                SteelVal::StringV("bar".into())
465            })
466            .into(),
467        );
468
469        let mut expected = HashSet::new();
470        expected.insert("foo".to_string());
471        expected.insert("bar".to_string());
472
473        assert_eq!(<HashSet<String>>::from_steelval(&input).unwrap(), expected);
474    }
475}