arithmetic_eval/fns/wrapper/
traits.rs

1//! Traits used in function wrapper.
2
3use core::{cmp, fmt};
4
5use crate::{
6    alloc::{vec, String, Vec},
7    CallContext, Error, ErrorKind, Function, Number, Value, ValueType,
8};
9
10/// Error raised when a value cannot be converted to the expected type when using
11/// [`FnWrapper`](crate::fns::FnWrapper).
12#[derive(Debug, Clone)]
13pub struct FromValueError {
14    kind: FromValueErrorKind,
15    arg_index: usize,
16    location: Vec<FromValueErrorLocation>,
17}
18
19impl FromValueError {
20    pub(crate) fn invalid_type<T>(expected: ValueType, actual_value: &Value<'_, T>) -> Self {
21        Self {
22            kind: FromValueErrorKind::InvalidType {
23                expected,
24                actual: actual_value.value_type(),
25            },
26            arg_index: 0,
27            location: vec![],
28        }
29    }
30
31    fn add_location(mut self, location: FromValueErrorLocation) -> Self {
32        self.location.push(location);
33        self
34    }
35
36    #[doc(hidden)] // necessary for `wrap_fn` macro
37    pub fn set_arg_index(&mut self, index: usize) {
38        self.arg_index = index;
39        self.location.reverse();
40    }
41
42    /// Returns the error kind.
43    pub fn kind(&self) -> &FromValueErrorKind {
44        &self.kind
45    }
46
47    /// Returns the zero-based index of the argument where the error has occurred.
48    pub fn arg_index(&self) -> usize {
49        self.arg_index
50    }
51
52    /// Returns the error location, starting from the outermost one.
53    pub fn location(&self) -> &[FromValueErrorLocation] {
54        &self.location
55    }
56}
57
58impl fmt::Display for FromValueError {
59    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
60        write!(
61            formatter,
62            "{}. Error location: arg{}",
63            self.kind, self.arg_index
64        )?;
65        for location_element in &self.location {
66            match location_element {
67                FromValueErrorLocation::Tuple { index, .. } => write!(formatter, ".{}", index)?,
68                FromValueErrorLocation::Array { index, .. } => write!(formatter, "[{}]", index)?,
69            }
70        }
71        Ok(())
72    }
73}
74
75#[cfg(feature = "std")]
76impl std::error::Error for FromValueError {}
77
78/// Error kinds for [`FromValueError`].
79#[derive(Debug, Clone, PartialEq)]
80#[non_exhaustive]
81pub enum FromValueErrorKind {
82    /// Mismatch between expected and actual value type.
83    InvalidType {
84        /// Expected value type.
85        expected: ValueType,
86        /// Actual value type.
87        actual: ValueType,
88    },
89}
90
91impl fmt::Display for FromValueErrorKind {
92    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
93        match self {
94            Self::InvalidType { expected, actual } => {
95                write!(formatter, "Cannot convert {} to {}", actual, expected)
96            }
97        }
98    }
99}
100
101/// Element of the [`FromValueError`] location.
102///
103/// Note that the distinction between tuples and arrays is determined by the [`FnWrapper`].
104/// If the corresponding type in the wrapper is defined as a tuple, then
105/// a [`Tuple`](FromValueErrorLocation::Tuple) element will be added to the location; otherwise,
106/// an [`Array`](FromValueErrorLocation::Array) will be added.
107///
108/// [`FnWrapper`]: crate::fns::FnWrapper
109#[derive(Debug, Clone, Copy, PartialEq)]
110#[non_exhaustive]
111pub enum FromValueErrorLocation {
112    /// Location within a tuple.
113    Tuple {
114        /// Tuple size.
115        size: usize,
116        /// Zero-based index of the erroneous element.
117        index: usize,
118    },
119    /// Location within an array.
120    Array {
121        /// Factual array size.
122        size: usize,
123        /// Zero-based index of the erroneous element.
124        index: usize,
125    },
126}
127
128/// Fallible conversion from `Value` to a function argument.
129///
130/// This trait is implemented for base value types (such as [`Number`]s, [`Function`]s, [`Value`]s),
131/// and for two container types: vectors and tuples.
132pub trait TryFromValue<'a, T>: Sized {
133    /// Attempts to convert `value` to a type supported by the function.
134    fn try_from_value(value: Value<'a, T>) -> Result<Self, FromValueError>;
135}
136
137impl<'a, T: Number> TryFromValue<'a, T> for T {
138    fn try_from_value(value: Value<'a, T>) -> Result<Self, FromValueError> {
139        match value {
140            Value::Prim(number) => Ok(number),
141            _ => Err(FromValueError::invalid_type(ValueType::Prim, &value)),
142        }
143    }
144}
145
146impl<'a, T> TryFromValue<'a, T> for bool {
147    fn try_from_value(value: Value<'a, T>) -> Result<Self, FromValueError> {
148        match value {
149            Value::Bool(flag) => Ok(flag),
150            _ => Err(FromValueError::invalid_type(ValueType::Bool, &value)),
151        }
152    }
153}
154
155impl<'a, T> TryFromValue<'a, T> for Value<'a, T> {
156    fn try_from_value(value: Value<'a, T>) -> Result<Self, FromValueError> {
157        Ok(value)
158    }
159}
160
161impl<'a, T> TryFromValue<'a, T> for Function<'a, T> {
162    fn try_from_value(value: Value<'a, T>) -> Result<Self, FromValueError> {
163        match value {
164            Value::Function(function) => Ok(function),
165            _ => Err(FromValueError::invalid_type(ValueType::Function, &value)),
166        }
167    }
168}
169
170impl<'a, U, T> TryFromValue<'a, T> for Vec<U>
171where
172    U: TryFromValue<'a, T>,
173{
174    fn try_from_value(value: Value<'a, T>) -> Result<Self, FromValueError> {
175        match value {
176            Value::Tuple(values) => {
177                let tuple_len = values.len();
178                let mut collected = Vec::with_capacity(tuple_len);
179
180                for (index, element) in values.into_iter().enumerate() {
181                    let converted = U::try_from_value(element).map_err(|err| {
182                        err.add_location(FromValueErrorLocation::Array {
183                            size: tuple_len,
184                            index,
185                        })
186                    })?;
187                    collected.push(converted);
188                }
189                Ok(collected)
190            }
191            _ => Err(FromValueError::invalid_type(ValueType::Array, &value)),
192        }
193    }
194}
195
196macro_rules! try_from_value_for_tuple {
197    ($size:expr => $($var:ident : $ty:ident),+) => {
198        impl<'a, Num, $($ty,)+> TryFromValue<'a, Num> for ($($ty,)+)
199        where
200            $($ty: TryFromValue<'a, Num>,)+
201        {
202            #[allow(clippy::shadow_unrelated)] // makes it easier to write macro
203            fn try_from_value(value: Value<'a, Num>) -> Result<Self, FromValueError> {
204                const EXPECTED_TYPE: ValueType = ValueType::Tuple($size);
205
206                match value {
207                    Value::Tuple(values) if values.len() == $size => {
208                        let mut values_iter = values.into_iter().enumerate();
209                        $(
210                            let (index, $var) = values_iter.next().unwrap();
211                            let $var = $ty::try_from_value($var).map_err(|err| {
212                                err.add_location(FromValueErrorLocation::Tuple {
213                                    size: $size,
214                                    index,
215                                })
216                            })?;
217                        )+
218                        Ok(($($var,)+))
219                    }
220                    _ => Err(FromValueError::invalid_type(EXPECTED_TYPE, &value)),
221                }
222            }
223        }
224    };
225}
226
227try_from_value_for_tuple!(1 => x0: T);
228try_from_value_for_tuple!(2 => x0: T, x1: U);
229try_from_value_for_tuple!(3 => x0: T, x1: U, x2: V);
230try_from_value_for_tuple!(4 => x0: T, x1: U, x2: V, x3: W);
231try_from_value_for_tuple!(5 => x0: T, x1: U, x2: V, x3: W, x4: X);
232try_from_value_for_tuple!(6 => x0: T, x1: U, x2: V, x3: W, x4: X, x5: Y);
233try_from_value_for_tuple!(7 => x0: T, x1: U, x2: V, x3: W, x4: X, x5: Y, x6: Z);
234try_from_value_for_tuple!(8 => x0: T, x1: U, x2: V, x3: W, x4: X, x5: Y, x6: Z, x7: A);
235try_from_value_for_tuple!(9 => x0: T, x1: U, x2: V, x3: W, x4: X, x5: Y, x6: Z, x7: A, x8: B);
236try_from_value_for_tuple!(10 => x0: T, x1: U, x2: V, x3: W, x4: X, x5: Y, x6: Z, x7: A, x8: B, x9: C);
237
238/// Generic error output encompassing all error types supported by
239/// [wrapped functions](crate::fns::FnWrapper).
240#[derive(Debug)]
241#[non_exhaustive]
242pub enum ErrorOutput<'a> {
243    /// Error together with the defined span(s).
244    Spanned(Error<'a>),
245    /// Error message. The error span will be defined as the call span of the native function.
246    Message(String),
247}
248
249impl<'a> ErrorOutput<'a> {
250    #[doc(hidden)] // necessary for `wrap_fn` macro
251    pub fn into_spanned<A>(self, context: &CallContext<'_, 'a, A>) -> Error<'a> {
252        match self {
253            Self::Spanned(err) => err,
254            Self::Message(message) => context.call_site_error(ErrorKind::native(message)),
255        }
256    }
257}
258
259/// Converts type into `Value` or an error. This is used to convert the return type
260/// of [wrapped functions](crate::fns::FnWrapper) to the result expected by
261/// [`NativeFn`](crate::NativeFn).
262///
263/// Unlike with `TryInto` trait from the standard library, the erroneous result here does not
264/// mean that the conversion *itself* is impossible. Rather, it means that the function evaluation
265/// has failed for the provided args.
266///
267///
268/// This trait is implemented for base value types (such as [`Number`]s, [`Function`]s, [`Value`]s),
269/// for two container types: vectors and tuples, and for `Result`s with the error type
270/// convertible to [`ErrorOutput`].
271pub trait IntoEvalResult<'a, T> {
272    /// Performs the conversion.
273    fn into_eval_result(self) -> Result<Value<'a, T>, ErrorOutput<'a>>;
274}
275
276impl<'a, T, U> IntoEvalResult<'a, T> for Result<U, String>
277where
278    U: IntoEvalResult<'a, T>,
279{
280    fn into_eval_result(self) -> Result<Value<'a, T>, ErrorOutput<'a>> {
281        self.map_err(ErrorOutput::Message)
282            .and_then(U::into_eval_result)
283    }
284}
285
286impl<'a, T, U> IntoEvalResult<'a, T> for Result<U, Error<'a>>
287where
288    U: IntoEvalResult<'a, T>,
289{
290    fn into_eval_result(self) -> Result<Value<'a, T>, ErrorOutput<'a>> {
291        self.map_err(ErrorOutput::Spanned)
292            .and_then(U::into_eval_result)
293    }
294}
295
296impl<'a, T: Number> IntoEvalResult<'a, T> for T {
297    fn into_eval_result(self) -> Result<Value<'a, T>, ErrorOutput<'a>> {
298        Ok(Value::Prim(self))
299    }
300}
301
302impl<'a, T> IntoEvalResult<'a, T> for () {
303    fn into_eval_result(self) -> Result<Value<'a, T>, ErrorOutput<'a>> {
304        Ok(Value::void())
305    }
306}
307
308impl<'a, T> IntoEvalResult<'a, T> for bool {
309    fn into_eval_result(self) -> Result<Value<'a, T>, ErrorOutput<'a>> {
310        Ok(Value::Bool(self))
311    }
312}
313
314impl<'a, T> IntoEvalResult<'a, T> for cmp::Ordering {
315    fn into_eval_result(self) -> Result<Value<'a, T>, ErrorOutput<'a>> {
316        Ok(Value::opaque_ref(self))
317    }
318}
319
320impl<'a, T> IntoEvalResult<'a, T> for Value<'a, T> {
321    fn into_eval_result(self) -> Result<Value<'a, T>, ErrorOutput<'a>> {
322        Ok(self)
323    }
324}
325
326impl<'a, T> IntoEvalResult<'a, T> for Function<'a, T> {
327    fn into_eval_result(self) -> Result<Value<'a, T>, ErrorOutput<'a>> {
328        Ok(Value::Function(self))
329    }
330}
331
332impl<'a, U, T> IntoEvalResult<'a, T> for Vec<U>
333where
334    U: IntoEvalResult<'a, T>,
335{
336    fn into_eval_result(self) -> Result<Value<'a, T>, ErrorOutput<'a>> {
337        let values = self
338            .into_iter()
339            .map(U::into_eval_result)
340            .collect::<Result<Vec<_>, _>>()?;
341        Ok(Value::Tuple(values))
342    }
343}
344
345macro_rules! into_value_for_tuple {
346    ($($i:tt : $ty:ident),+) => {
347        impl<'a, Num, $($ty,)+> IntoEvalResult<'a, Num> for ($($ty,)+)
348        where
349            $($ty: IntoEvalResult<'a, Num>,)+
350        {
351            fn into_eval_result(self) -> Result<Value<'a, Num>, ErrorOutput<'a>> {
352                Ok(Value::Tuple(vec![$(self.$i.into_eval_result()?,)+]))
353            }
354        }
355    };
356}
357
358into_value_for_tuple!(0: T);
359into_value_for_tuple!(0: T, 1: U);
360into_value_for_tuple!(0: T, 1: U, 2: V);
361into_value_for_tuple!(0: T, 1: U, 2: V, 3: W);
362into_value_for_tuple!(0: T, 1: U, 2: V, 3: W, 4: X);
363into_value_for_tuple!(0: T, 1: U, 2: V, 3: W, 4: X, 5: Y);
364into_value_for_tuple!(0: T, 1: U, 2: V, 3: W, 4: X, 5: Y, 6: Z);
365into_value_for_tuple!(0: T, 1: U, 2: V, 3: W, 4: X, 5: Y, 6: Z, 7: A);
366into_value_for_tuple!(0: T, 1: U, 2: V, 3: W, 4: X, 5: Y, 6: Z, 7: A, 8: B);
367into_value_for_tuple!(0: T, 1: U, 2: V, 3: W, 4: X, 5: Y, 6: Z, 7: A, 8: B, 9: C);