ruby_marshal/convert/
from_value.rs

1use super::DisplayByteString;
2use crate::ArrayValue;
3use crate::BoolValue;
4use crate::FixnumValue;
5use crate::HashValue;
6use crate::NilValue;
7use crate::ObjectValue;
8use crate::StringValue;
9use crate::SymbolValue;
10use crate::UserDefinedValue;
11use crate::Value;
12use crate::ValueArena;
13use crate::ValueHandle;
14use crate::ValueKind;
15use std::cell::RefCell;
16use std::collections::BTreeMap;
17use std::collections::HashMap;
18use std::hash::Hash;
19
20/// An error that may occur while creating a type from a Ruby Value.
21#[derive(Debug)]
22pub enum FromValueError {
23    /// An already visited node was visited.
24    Cycle {
25        /// The already-visited node.
26        handle: ValueHandle,
27    },
28
29    /// A given [`ValueHandle`] was invalid.
30    InvalidValueHandle {
31        /// The invalid handle
32        handle: ValueHandle,
33    },
34
35    /// An unexpected value kind was encountered.
36    UnexpectedValueKind {
37        /// The unexpected value kind
38        kind: ValueKind,
39
40        /// The current path of value handles
41        trace: Vec<ValueHandle>,
42    },
43
44    /// An object name was unexpected.
45    UnexpectedObjectName {
46        /// The object name.
47        ///
48        /// This may or may not be UTF-8.
49        name: Vec<u8>,
50    },
51
52    /// A user defined value name was unexpected.
53    UnexpectedUserDefinedName {
54        /// The user defined name.
55        ///
56        /// This may or may not be UTF-8.
57        name: Vec<u8>,
58    },
59
60    /// An instance variable was duplicated
61    DuplicateInstanceVariable {
62        /// The instance variable name.
63        ///
64        /// This may or may not be UTF-8.
65        name: Vec<u8>,
66    },
67
68    /// An unknown instance variable was encountered.
69    UnknownInstanceVariable {
70        /// The instance variable name.
71        ///
72        /// This may or may not be UTF-8.
73        name: Vec<u8>,
74    },
75
76    /// Missing an instance variable with the given name.
77    MissingInstanceVariable {
78        /// The instance variable name.
79        ///
80        /// This may or may not be UTF-8.
81        name: Vec<u8>,
82    },
83
84    /// A hash key was provided twice.
85    DuplicateHashKey {
86        /// The key that was provided twice.
87        ///
88        /// This does not need to be a symbol.
89        key: ValueHandle,
90    },
91
92    /// Another user-provided kind of error occured.
93    Other {
94        error: Box<dyn std::error::Error + Send + Sync + 'static>,
95    },
96}
97
98impl FromValueError {
99    /// Shorthand for creating a new `Other` error variant.
100    pub fn new_other<E>(error: E) -> Self
101    where
102        E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
103    {
104        Self::Other {
105            error: error.into(),
106        }
107    }
108}
109
110impl std::fmt::Display for FromValueError {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        match self {
113            Self::Cycle { .. } => write!(f, "attempted to extract recursively"),
114            Self::InvalidValueHandle { .. } => write!(f, "a handle was invalid"),
115            Self::UnexpectedValueKind { kind, .. } => write!(f, "unexpected value kind {kind:?}"),
116            Self::UnexpectedObjectName { name } => {
117                write!(f, "unexpected object name \"{}\"", DisplayByteString(name))
118            }
119            Self::UnexpectedUserDefinedName { name } => {
120                write!(
121                    f,
122                    "unexpected user defined name \"{}\"",
123                    DisplayByteString(name)
124                )
125            }
126            Self::DuplicateInstanceVariable { name } => {
127                write!(
128                    f,
129                    "instance variable \"{}\" was encountered twice",
130                    DisplayByteString(name)
131                )
132            }
133            Self::UnknownInstanceVariable { name } => {
134                write!(
135                    f,
136                    "instance variable \"{}\" is not known",
137                    DisplayByteString(name)
138                )
139            }
140            Self::MissingInstanceVariable { name } => {
141                write!(
142                    f,
143                    "instance variable \"{}\" is missing",
144                    DisplayByteString(name)
145                )
146            }
147            Self::DuplicateHashKey { .. } => {
148                write!(f, "duplicate hash key")
149            }
150            Self::Other { .. } => write!(f, "a user-provided error was encountered"),
151        }
152    }
153}
154
155impl std::error::Error for FromValueError {
156    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
157        match self {
158            Self::Other { error } => Some(&**error),
159            _ => None,
160        }
161    }
162}
163
164/// A context to manage extracting values.
165pub struct FromValueContext<'a> {
166    arena: &'a ValueArena,
167    stack: RefCell<Vec<ValueHandle>>,
168}
169
170impl<'a> FromValueContext<'a> {
171    /// Create a new context from an arena.
172    pub fn new(arena: &'a ValueArena) -> Self {
173        Self {
174            arena,
175            stack: RefCell::new(Vec::new()),
176        }
177    }
178
179    fn begin_handle(&self, handle: ValueHandle) -> Result<(), FromValueError> {
180        let mut stack = self.stack.borrow_mut();
181
182        if stack.contains(&handle) {
183            return Err(FromValueError::Cycle { handle });
184        }
185
186        stack.push(handle);
187
188        Ok(())
189    }
190
191    fn end_handle(&self, handle: ValueHandle) {
192        let stack_handle = self.stack.borrow_mut().pop();
193
194        // This should always be Some.
195        let stack_handle = stack_handle.unwrap();
196
197        assert!(handle == stack_handle);
198    }
199
200    // The "value" here is a represented by the value handle.
201    #[allow(clippy::wrong_self_convention)]
202    /// Extract a type from a value.
203    pub fn from_value<T>(&self, handle: ValueHandle) -> Result<T, FromValueError>
204    where
205        T: FromValue<'a>,
206    {
207        let guard = FromValueGuard::new(self, handle)?;
208        let value = self
209            .arena
210            .get(handle)
211            .ok_or(FromValueError::InvalidValueHandle { handle })?;
212        let value = T::from_value(self, value)?;
213        drop(guard);
214
215        Ok(value)
216    }
217
218    /// Create a new UnexpectedValueKind error
219    pub fn new_unexpected_value_kind_error(&self, kind: ValueKind) -> FromValueError {
220        FromValueError::UnexpectedValueKind {
221            kind,
222            trace: self.stack.borrow().clone(),
223        }
224    }
225}
226
227/// A guard for a handle.
228///
229/// Do NOT drop this before you are done using the handle and its children.
230pub struct FromValueGuard<'a, 'b> {
231    ctx: &'a FromValueContext<'b>,
232    handle: ValueHandle,
233}
234
235impl<'a, 'b> FromValueGuard<'a, 'b> {
236    fn new(ctx: &'a FromValueContext<'b>, handle: ValueHandle) -> Result<Self, FromValueError> {
237        ctx.begin_handle(handle)?;
238
239        Ok(Self { ctx, handle })
240    }
241}
242
243impl Drop for FromValueGuard<'_, '_> {
244    fn drop(&mut self) {
245        self.ctx.end_handle(self.handle);
246    }
247}
248
249/// Implemented for any type that can be created from a Ruby Value.
250pub trait FromValue<'a>: Sized {
251    /// Create this type from the given value from the [`ValueArena`].
252    ///
253    /// # Arguments
254    /// 1. `ctx`: The value extraction context.
255    /// 2. `handle`: The handle that points to the value to convert.
256    fn from_value(ctx: &FromValueContext<'a>, value: &'a Value) -> Result<Self, FromValueError>;
257}
258
259impl<'a> FromValue<'a> for &'a Value {
260    fn from_value(_ctx: &FromValueContext<'a>, value: &'a Value) -> Result<Self, FromValueError> {
261        Ok(value)
262    }
263}
264
265impl<'a> FromValue<'a> for &'a NilValue {
266    fn from_value(ctx: &FromValueContext<'a>, value: &'a Value) -> Result<Self, FromValueError> {
267        match value {
268            Value::Nil(value) => Ok(value),
269            value => Err(ctx.new_unexpected_value_kind_error(value.kind())),
270        }
271    }
272}
273
274impl<'a> FromValue<'a> for &'a BoolValue {
275    fn from_value(ctx: &FromValueContext<'a>, value: &'a Value) -> Result<Self, FromValueError> {
276        match value {
277            Value::Bool(value) => Ok(value),
278            value => Err(ctx.new_unexpected_value_kind_error(value.kind())),
279        }
280    }
281}
282
283impl<'a> FromValue<'a> for &'a FixnumValue {
284    fn from_value(ctx: &FromValueContext<'a>, value: &'a Value) -> Result<Self, FromValueError> {
285        match value {
286            Value::Fixnum(value) => Ok(value),
287            value => Err(ctx.new_unexpected_value_kind_error(value.kind())),
288        }
289    }
290}
291
292impl<'a> FromValue<'a> for &'a SymbolValue {
293    fn from_value(ctx: &FromValueContext<'a>, value: &'a Value) -> Result<Self, FromValueError> {
294        match value {
295            Value::Symbol(value) => Ok(value),
296            value => Err(ctx.new_unexpected_value_kind_error(value.kind())),
297        }
298    }
299}
300
301impl<'a> FromValue<'a> for &'a ArrayValue {
302    fn from_value(ctx: &FromValueContext<'a>, value: &'a Value) -> Result<Self, FromValueError> {
303        match value {
304            Value::Array(value) => Ok(value),
305            value => Err(ctx.new_unexpected_value_kind_error(value.kind())),
306        }
307    }
308}
309
310impl<'a> FromValue<'a> for &'a HashValue {
311    fn from_value(ctx: &FromValueContext<'a>, value: &'a Value) -> Result<Self, FromValueError> {
312        match value {
313            Value::Hash(value) => Ok(value),
314            value => Err(ctx.new_unexpected_value_kind_error(value.kind())),
315        }
316    }
317}
318
319impl<'a> FromValue<'a> for &'a ObjectValue {
320    fn from_value(ctx: &FromValueContext<'a>, value: &'a Value) -> Result<Self, FromValueError> {
321        match value {
322            Value::Object(value) => Ok(value),
323            value => Err(ctx.new_unexpected_value_kind_error(value.kind())),
324        }
325    }
326}
327
328impl<'a> FromValue<'a> for &'a StringValue {
329    fn from_value(ctx: &FromValueContext<'a>, value: &'a Value) -> Result<Self, FromValueError> {
330        match value {
331            Value::String(value) => Ok(value),
332            value => Err(ctx.new_unexpected_value_kind_error(value.kind())),
333        }
334    }
335}
336
337impl<'a> FromValue<'a> for &'a UserDefinedValue {
338    fn from_value(ctx: &FromValueContext<'a>, value: &'a Value) -> Result<Self, FromValueError> {
339        match value {
340            Value::UserDefined(value) => Ok(value),
341            value => Err(ctx.new_unexpected_value_kind_error(value.kind())),
342        }
343    }
344}
345
346impl<'a> FromValue<'a> for bool {
347    fn from_value(ctx: &FromValueContext<'a>, value: &'a Value) -> Result<Self, FromValueError> {
348        let value: &BoolValue = FromValue::from_value(ctx, value)?;
349        Ok(value.value())
350    }
351}
352
353impl<'a> FromValue<'a> for i32 {
354    fn from_value(ctx: &FromValueContext<'a>, value: &'a Value) -> Result<Self, FromValueError> {
355        let value: &FixnumValue = FromValue::from_value(ctx, value)?;
356        Ok(value.value())
357    }
358}
359
360impl<'a, T> FromValue<'a> for Option<T>
361where
362    T: FromValue<'a>,
363{
364    fn from_value(ctx: &FromValueContext<'a>, value: &'a Value) -> Result<Self, FromValueError> {
365        match value {
366            Value::Nil(_) => Ok(None),
367            _ => T::from_value(ctx, value).map(Some),
368        }
369    }
370}
371
372impl<'a, T> FromValue<'a> for Vec<T>
373where
374    T: FromValue<'a>,
375{
376    fn from_value(ctx: &FromValueContext<'a>, value: &'a Value) -> Result<Self, FromValueError> {
377        let array: &ArrayValue = FromValue::from_value(ctx, value)?;
378        let array = array.value();
379
380        let mut vec = Vec::with_capacity(array.len());
381        for handle in array.iter().copied() {
382            let value = ctx.from_value(handle)?;
383            vec.push(value);
384        }
385
386        Ok(vec)
387    }
388}
389
390/// An error that may occur while extracting a HashMap from a value.
391#[derive(Debug)]
392pub enum HashMapFromValueError {
393    /// The HashMap cannot be extracted since it has a default value.
394    HasDefaultValue {
395        /// The default value
396        value: ValueHandle,
397    },
398}
399
400impl std::fmt::Display for HashMapFromValueError {
401    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
402        match self {
403            Self::HasDefaultValue { .. } => {
404                write!(f, "HashValue has a default value")
405            }
406        }
407    }
408}
409
410impl std::error::Error for HashMapFromValueError {}
411
412impl<'a, K, V> FromValue<'a> for HashMap<K, V>
413where
414    K: FromValue<'a> + Hash + Eq,
415    V: FromValue<'a>,
416{
417    fn from_value(ctx: &FromValueContext<'a>, value: &'a Value) -> Result<Self, FromValueError> {
418        let value: &HashValue = FromValue::from_value(ctx, value)?;
419
420        if let Some(default_value) = value.default_value() {
421            return Err(FromValueError::new_other(
422                HashMapFromValueError::HasDefaultValue {
423                    value: default_value,
424                },
425            ));
426        }
427
428        let value = value.value();
429
430        let mut map = HashMap::with_capacity(value.len());
431        for (key_handle, value_handle) in value.iter().copied() {
432            let key = ctx.from_value(key_handle)?;
433            let value = ctx.from_value(value_handle)?;
434
435            let old_value = map.insert(key, value);
436
437            if old_value.is_some() {
438                return Err(FromValueError::DuplicateHashKey { key: key_handle });
439            }
440        }
441
442        Ok(map)
443    }
444}
445
446/// An error that may occur while extracting a BTreeMap from a value.
447#[derive(Debug)]
448pub enum BTreeMapFromValueError {
449    /// The BTreeMap cannot be extracted since it has a default value.
450    HasDefaultValue {
451        /// The default value
452        value: ValueHandle,
453    },
454}
455
456impl std::fmt::Display for BTreeMapFromValueError {
457    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
458        match self {
459            Self::HasDefaultValue { .. } => {
460                write!(f, "HashValue has a default value")
461            }
462        }
463    }
464}
465
466impl std::error::Error for BTreeMapFromValueError {}
467
468impl<'a, K, V> FromValue<'a> for BTreeMap<K, V>
469where
470    K: FromValue<'a> + Eq + Ord,
471    V: FromValue<'a>,
472{
473    fn from_value(ctx: &FromValueContext<'a>, value: &'a Value) -> Result<Self, FromValueError> {
474        let value: &HashValue = FromValue::from_value(ctx, value)?;
475
476        if let Some(default_value) = value.default_value() {
477            return Err(FromValueError::new_other(
478                BTreeMapFromValueError::HasDefaultValue {
479                    value: default_value,
480                },
481            ));
482        }
483
484        let value = value.value();
485
486        let mut map = BTreeMap::new();
487        for (key_handle, value_handle) in value.iter().copied() {
488            let key = ctx.from_value(key_handle)?;
489            let value = ctx.from_value(value_handle)?;
490
491            let old_value = map.insert(key, value);
492
493            if old_value.is_some() {
494                return Err(FromValueError::DuplicateHashKey { key: key_handle });
495            }
496        }
497
498        Ok(map)
499    }
500}