artichoke_backend/
convert.rs

1use std::borrow::Cow;
2use std::error;
3use std::fmt;
4
5use spinoso_exception::TypeError;
6
7use crate::Artichoke;
8use crate::core::{ClassRegistry, Convert, ConvertMut, TryConvert, TryConvertMut, Value as _};
9use crate::error::{Error, RubyException};
10use crate::sys;
11use crate::types::{Ruby, Rust};
12use crate::value::Value;
13
14mod array;
15mod boolean;
16mod boxing;
17mod bytes;
18mod conv;
19mod fixnum;
20mod float;
21mod float_to_int;
22mod hash;
23mod implicit;
24mod maybe_to_int;
25mod nilable;
26mod string;
27
28pub use boxing::{BoxUnboxVmValue, HeapAllocated, HeapAllocatedData, Immediate, UnboxedValueGuard};
29pub use conv::{
30    ConvertOnError, check_string_type, check_to_a, check_to_ary, check_to_int, check_to_str, convert_type, to_a,
31    to_ary, to_i, to_int, to_str,
32};
33pub use float_to_int::float_to_int;
34pub use implicit::{
35    implicitly_convert_to_int, implicitly_convert_to_nilable_string, implicitly_convert_to_spinoso_string,
36    implicitly_convert_to_string,
37};
38pub use maybe_to_int::{MaybeToInt, maybe_to_int};
39
40/// Provide a fallible converter for types that implement an infallible
41/// conversion.
42impl<T, U> TryConvert<T, U> for Artichoke
43where
44    Artichoke: Convert<T, U>,
45{
46    // TODO: this should be the never type.
47    // https://github.com/rust-lang/rust/issues/35121
48    type Error = Error;
49
50    /// Blanket implementation that always succeeds by delegating to
51    /// [`Convert::convert`].
52    #[inline]
53    fn try_convert(&self, value: T) -> Result<U, Self::Error> {
54        Ok(Convert::convert(self, value))
55    }
56}
57
58/// Provide a mutable fallible converter for types that implement an infallible
59/// conversion.
60impl<T, U> TryConvertMut<T, U> for Artichoke
61where
62    Artichoke: ConvertMut<T, U>,
63{
64    // TODO: this should be the never type.
65    // https://github.com/rust-lang/rust/issues/35121
66    type Error = Error;
67
68    /// Blanket implementation that always succeeds by delegating to
69    /// [`Convert::convert`].
70    #[inline]
71    fn try_convert_mut(&mut self, value: T) -> Result<U, Self::Error> {
72        Ok(ConvertMut::convert_mut(self, value))
73    }
74}
75
76/// Failed to convert from boxed Ruby value to a Rust type.
77#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
78pub struct UnboxRubyError {
79    pub from: Ruby,
80    pub into: Rust,
81}
82
83impl UnboxRubyError {
84    #[must_use]
85    #[inline]
86    pub fn new(value: &Value, into: Rust) -> Self {
87        Self {
88            from: value.ruby_type(),
89            into,
90        }
91    }
92}
93
94impl fmt::Display for UnboxRubyError {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        write!(f, "failed to convert from {} to {}", self.from, self.into)
97    }
98}
99
100impl error::Error for UnboxRubyError {}
101
102impl RubyException for UnboxRubyError {
103    fn message(&self) -> Cow<'_, [u8]> {
104        Cow::Borrowed(b"Failed to convert from Ruby value to Rust type")
105    }
106
107    fn name(&self) -> Cow<'_, str> {
108        "TypeError".into()
109    }
110
111    fn vm_backtrace(&self, interp: &mut Artichoke) -> Option<Vec<Vec<u8>>> {
112        let _ = interp;
113        None
114    }
115
116    fn as_mrb_value(&self, interp: &mut Artichoke) -> Option<sys::mrb_value> {
117        let message = interp.try_convert_mut(self.to_string()).ok()?;
118        let value = interp.new_instance::<TypeError>(&[message]).ok().flatten()?;
119        Some(value.inner())
120    }
121}
122
123impl From<UnboxRubyError> for Error {
124    fn from(exception: UnboxRubyError) -> Self {
125        let err: Box<dyn RubyException> = Box::new(exception);
126        Self::from(err)
127    }
128}
129
130/// Failed to convert from Rust type to a boxed Ruby value.
131#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
132pub struct BoxIntoRubyError {
133    pub from: Rust,
134    pub into: Ruby,
135}
136
137impl BoxIntoRubyError {
138    #[must_use]
139    #[inline]
140    pub fn new(from: Rust, into: Ruby) -> Self {
141        Self { from, into }
142    }
143}
144
145impl fmt::Display for BoxIntoRubyError {
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        write!(f, "failed to convert from {} to {}", self.from, self.into)
148    }
149}
150
151impl error::Error for BoxIntoRubyError {}
152
153impl RubyException for BoxIntoRubyError {
154    fn message(&self) -> Cow<'_, [u8]> {
155        Cow::Borrowed(b"Failed to convert from Rust type to Ruby value")
156    }
157
158    fn name(&self) -> Cow<'_, str> {
159        "TypeError".into()
160    }
161
162    fn vm_backtrace(&self, interp: &mut Artichoke) -> Option<Vec<Vec<u8>>> {
163        let _ = interp;
164        None
165    }
166
167    fn as_mrb_value(&self, interp: &mut Artichoke) -> Option<sys::mrb_value> {
168        let message = interp.try_convert_mut(self.to_string()).ok()?;
169        let value = interp.new_instance::<TypeError>(&[message]).ok().flatten()?;
170        Some(value.inner())
171    }
172}
173
174impl From<BoxIntoRubyError> for Error {
175    fn from(exception: BoxIntoRubyError) -> Self {
176        let err: Box<dyn RubyException> = Box::new(exception);
177        Self::from(err)
178    }
179}