ruby_marshal/
convert.rs

1mod from_value;
2
3pub use self::from_value::BTreeMapFromValueError;
4pub use self::from_value::FromValue;
5pub use self::from_value::FromValueContext;
6pub use self::from_value::FromValueError;
7pub use self::from_value::HashMapFromValueError;
8use crate::ValueArena;
9use crate::ValueHandle;
10use std::collections::BTreeMap;
11use std::collections::HashMap;
12
13/// A utility to display a byte sequence as a string if it is UTF8 or a slice otherwise.
14#[derive(Debug)]
15pub struct DisplayByteString<'a>(pub &'a [u8]);
16
17impl std::fmt::Display for DisplayByteString<'_> {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        let string = self.0;
20        match std::str::from_utf8(string) {
21            Ok(string) => write!(f, "{string}"),
22            Err(_error) => write!(f, "{string:?}"),
23        }
24    }
25}
26
27/// An error that may occur while transforming types into Ruby Values.
28#[derive(Debug)]
29pub enum IntoValueError {
30    /// Another user-provided kind of error occured.
31    Other {
32        error: Box<dyn std::error::Error + Send + Sync + 'static>,
33    },
34}
35
36impl IntoValueError {
37    /// Shorthand for creating a new `Other` error variant.
38    pub fn new_other<E>(error: E) -> Self
39    where
40        E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
41    {
42        Self::Other {
43            error: error.into(),
44        }
45    }
46}
47
48impl std::fmt::Display for IntoValueError {
49    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        match self {
51            Self::Other { .. } => write!(f, "a user-provided error was encountered"),
52        }
53    }
54}
55
56impl std::error::Error for IntoValueError {
57    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
58        match self {
59            Self::Other { error } => Some(&**error),
60            // _ => None,
61        }
62    }
63}
64
65/// Implemented for any type that can be converted into a Ruby Value.
66pub trait IntoValue: Sized {
67    /// Turn this type into a Ruby Value.
68    fn into_value(self, arena: &mut ValueArena) -> Result<ValueHandle, IntoValueError>;
69}
70
71impl IntoValue for bool {
72    fn into_value(self, arena: &mut ValueArena) -> Result<ValueHandle, IntoValueError> {
73        Ok(arena.create_bool(self).into())
74    }
75}
76
77impl IntoValue for i32 {
78    fn into_value(self, arena: &mut ValueArena) -> Result<ValueHandle, IntoValueError> {
79        Ok(arena.create_fixnum(self).into())
80    }
81}
82
83impl<T> IntoValue for Vec<T>
84where
85    T: IntoValue,
86{
87    fn into_value(self, arena: &mut ValueArena) -> Result<ValueHandle, IntoValueError> {
88        let mut array = Vec::with_capacity(self.len());
89        for item in self.into_iter() {
90            array.push(item.into_value(arena)?);
91        }
92        Ok(arena.create_array(array).into())
93    }
94}
95
96impl<K, V> IntoValue for HashMap<K, V>
97where
98    K: IntoValue,
99    V: IntoValue,
100{
101    fn into_value(self, arena: &mut ValueArena) -> Result<ValueHandle, IntoValueError> {
102        let mut items = Vec::new();
103
104        for (key, value) in self.into_iter() {
105            let key_handle = key.into_value(arena)?;
106            let value_handle = value.into_value(arena)?;
107
108            items.push((key_handle, value_handle));
109        }
110
111        Ok(arena.create_hash(items, None).into())
112    }
113}
114
115impl<K, V> IntoValue for BTreeMap<K, V>
116where
117    K: IntoValue,
118    V: IntoValue,
119{
120    fn into_value(self, arena: &mut ValueArena) -> Result<ValueHandle, IntoValueError> {
121        let mut items = Vec::new();
122
123        for (key, value) in self.into_iter() {
124            let key_handle = key.into_value(arena)?;
125            let value_handle = value.into_value(arena)?;
126
127            items.push((key_handle, value_handle));
128        }
129
130        Ok(arena.create_hash(items, None).into())
131    }
132}
133
134impl<T> IntoValue for Option<T>
135where
136    T: IntoValue,
137{
138    fn into_value(self, arena: &mut ValueArena) -> Result<ValueHandle, IntoValueError> {
139        match self {
140            Some(value) => value.into_value(arena),
141            None => Ok(arena.create_nil().into()),
142        }
143    }
144}
145
146#[cfg(test)]
147mod test {
148    use super::*;
149    use crate::ArrayValue;
150    use crate::BoolValue;
151    use crate::FixnumValue;
152    use crate::HashValue;
153    use crate::NilValue;
154    use crate::ObjectValue;
155    use crate::StringValue;
156    use crate::SymbolValue;
157    use crate::UserDefinedValue;
158    use crate::Value;
159
160    #[test]
161    fn sanity() {
162        let mut arena = ValueArena::new();
163
164        let nil_handle = arena.create_nil().into_raw();
165        let bool_handle = arena.create_bool(true).into_raw();
166        let fixnum_handle = arena.create_fixnum(23).into_raw();
167        let symbol_handle = arena.create_symbol("symbol".into());
168        let array_handle = arena.create_array(vec![fixnum_handle]).into_raw();
169        let hash_handle = arena.create_hash(Vec::new(), None).into_raw();
170        let object_handle = arena.create_object(symbol_handle, Vec::new()).into_raw();
171        let string_handle = arena.create_string("string".into()).into_raw();
172        let user_defined_handle = arena
173            .create_user_defined(symbol_handle, Vec::new())
174            .into_raw();
175
176        let symbol_handle = symbol_handle.into_raw();
177
178        let ctx = FromValueContext::new(&arena);
179
180        let _value: &Value = ctx
181            .from_value(nil_handle)
182            .expect("failed to exec &Value::from_value");
183
184        let _nil_value: &NilValue = ctx
185            .from_value(nil_handle)
186            .expect("failed exec &NilValue::from_value");
187
188        let _bool_value: &BoolValue = ctx
189            .from_value(bool_handle)
190            .expect("failed exec &BoolValue::from_value");
191
192        let _fixnum_value: &FixnumValue = ctx
193            .from_value(fixnum_handle)
194            .expect("failed exec &FixnumValue::from_value");
195
196        let _symbol_value: &SymbolValue = ctx
197            .from_value(symbol_handle)
198            .expect("failed exec &SymbolValue::from_value");
199
200        let _array_value: &ArrayValue = ctx
201            .from_value(array_handle)
202            .expect("failed exec &ArrayValue::from_value");
203
204        let _hash_value: &HashValue = ctx
205            .from_value(hash_handle)
206            .expect("failed exec &HashValue::from_value");
207
208        let _string_value: &ObjectValue = ctx
209            .from_value(object_handle)
210            .expect("failed exec &ObjectValue::from_value");
211
212        let _string_value: &StringValue = ctx
213            .from_value(string_handle)
214            .expect("failed exec &StringValue::from_value");
215
216        let _user_defined_value: &UserDefinedValue = ctx
217            .from_value(user_defined_handle)
218            .expect("failed exec &UserDefinedValue::from_value");
219
220        let _bool_value: bool = ctx
221            .from_value(bool_handle)
222            .expect("failed exec bool::from_value");
223
224        let _i32_value: i32 = ctx
225            .from_value(fixnum_handle)
226            .expect("failed exec i32::from_value");
227
228        let _some_symbol_value: Option<&SymbolValue> = ctx
229            .from_value(symbol_handle)
230            .expect("failed exec Option<&SymbolValue>::from_value");
231
232        let _none_symbol_value: Option<&SymbolValue> = ctx
233            .from_value(nil_handle)
234            .expect("failed exec Option<&SymbolValue>::from_value");
235
236        let _vec_value: Vec<i32> = ctx
237            .from_value(array_handle)
238            .expect("failed exec <Vec<i32>>::from_value");
239
240        let _hash_map_value: HashMap<i32, i32> = ctx
241            .from_value(hash_handle)
242            .expect("failed exec <HashMap<i32, i32>>::from_value");
243
244        let _btree_map_value: BTreeMap<i32, i32> = ctx
245            .from_value(hash_handle)
246            .expect("failed exec <BTreeMap<i32, i32>>::from_value");
247
248        true.into_value(&mut arena)
249            .expect("failed to exec bool::into_value");
250
251        0_i32
252            .into_value(&mut arena)
253            .expect("failed to exec i32::into_value");
254
255        vec![0, 1, 2]
256            .into_value(&mut arena)
257            .expect("failed to exec Vec::<i32>::into_value");
258
259        HashMap::<i32, i32>::new()
260            .into_value(&mut arena)
261            .expect("failed to exec HashMap::<i32, i32>::into_value");
262
263        BTreeMap::<i32, i32>::new()
264            .into_value(&mut arena)
265            .expect("failed to exec BTreeMap::<i32, i32>::into_value");
266
267        Some(2_i32)
268            .into_value(&mut arena)
269            .expect("failed to exec Option::<i32>::Some::into_value");
270
271        None::<i32>
272            .into_value(&mut arena)
273            .expect("failed to exec Option::<i32>::None::into_value");
274    }
275}