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, Object, Tuple, 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, Eq)]
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 {actual} to {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, Eq)]
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<T>: Sized {
133    /// Attempts to convert `value` to a type supported by the function.
134    fn try_from_value(value: Value<T>) -> Result<Self, FromValueError>;
135}
136
137impl<T: Number> TryFromValue<T> for T {
138    fn try_from_value(value: Value<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<T> TryFromValue<T> for bool {
147    fn try_from_value(value: Value<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<T> TryFromValue<T> for Value<T> {
156    fn try_from_value(value: Value<T>) -> Result<Self, FromValueError> {
157        Ok(value)
158    }
159}
160
161impl<T> TryFromValue<T> for Function<T> {
162    fn try_from_value(value: Value<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<T> TryFromValue<T> for Tuple<T> {
171    fn try_from_value(value: Value<T>) -> Result<Self, FromValueError> {
172        match value {
173            Value::Tuple(tuple) => Ok(tuple),
174            _ => Err(FromValueError::invalid_type(ValueType::Array, &value)),
175        }
176    }
177}
178
179impl<T> TryFromValue<T> for Object<T> {
180    fn try_from_value(value: Value<T>) -> Result<Self, FromValueError> {
181        match value {
182            Value::Object(object) => Ok(object),
183            _ => Err(FromValueError::invalid_type(ValueType::Object, &value)),
184        }
185    }
186}
187
188impl<U, T> TryFromValue<T> for Vec<U>
189where
190    U: TryFromValue<T>,
191{
192    fn try_from_value(value: Value<T>) -> Result<Self, FromValueError> {
193        match value {
194            Value::Tuple(values) => {
195                let tuple_len = values.len();
196                let mut collected = Vec::with_capacity(tuple_len);
197
198                for (index, element) in values.into_iter().enumerate() {
199                    let converted = U::try_from_value(element).map_err(|err| {
200                        err.add_location(FromValueErrorLocation::Array {
201                            size: tuple_len,
202                            index,
203                        })
204                    })?;
205                    collected.push(converted);
206                }
207                Ok(collected)
208            }
209            _ => Err(FromValueError::invalid_type(ValueType::Array, &value)),
210        }
211    }
212}
213
214macro_rules! try_from_value_for_tuple {
215    ($size:expr => $($var:ident : $ty:ident),+) => {
216        impl<Num, $($ty,)+> TryFromValue<Num> for ($($ty,)+)
217        where
218            $($ty: TryFromValue<Num>,)+
219        {
220            #[allow(clippy::shadow_unrelated)] // makes it easier to write macro
221            fn try_from_value(value: Value<Num>) -> Result<Self, FromValueError> {
222                const EXPECTED_TYPE: ValueType = ValueType::Tuple($size);
223
224                match value {
225                    Value::Tuple(values) if values.len() == $size => {
226                        let mut values_iter = values.into_iter().enumerate();
227                        $(
228                            let (index, $var) = values_iter.next().unwrap();
229                            let $var = $ty::try_from_value($var).map_err(|err| {
230                                err.add_location(FromValueErrorLocation::Tuple {
231                                    size: $size,
232                                    index,
233                                })
234                            })?;
235                        )+
236                        Ok(($($var,)+))
237                    }
238                    _ => Err(FromValueError::invalid_type(EXPECTED_TYPE, &value)),
239                }
240            }
241        }
242    };
243}
244
245try_from_value_for_tuple!(1 => x0: T);
246try_from_value_for_tuple!(2 => x0: T, x1: U);
247try_from_value_for_tuple!(3 => x0: T, x1: U, x2: V);
248try_from_value_for_tuple!(4 => x0: T, x1: U, x2: V, x3: W);
249try_from_value_for_tuple!(5 => x0: T, x1: U, x2: V, x3: W, x4: X);
250try_from_value_for_tuple!(6 => x0: T, x1: U, x2: V, x3: W, x4: X, x5: Y);
251try_from_value_for_tuple!(7 => x0: T, x1: U, x2: V, x3: W, x4: X, x5: Y, x6: Z);
252try_from_value_for_tuple!(8 => x0: T, x1: U, x2: V, x3: W, x4: X, x5: Y, x6: Z, x7: A);
253try_from_value_for_tuple!(9 => x0: T, x1: U, x2: V, x3: W, x4: X, x5: Y, x6: Z, x7: A, x8: B);
254try_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);
255
256/// Generic error output encompassing all error types supported by
257/// [wrapped functions](crate::fns::FnWrapper).
258#[derive(Debug)]
259#[non_exhaustive]
260pub enum ErrorOutput {
261    /// Error together with the defined span(s).
262    Spanned(Error),
263    /// Error message. The error span will be defined as the call span of the native function.
264    Message(String),
265}
266
267impl ErrorOutput {
268    #[doc(hidden)] // necessary for `wrap_fn` macro
269    pub fn into_spanned<A>(self, context: &CallContext<'_, A>) -> Error {
270        match self {
271            Self::Spanned(err) => err,
272            Self::Message(message) => context.call_site_error(ErrorKind::native(message)),
273        }
274    }
275}
276
277/// Converts type into `Value` or an error. This is used to convert the return type
278/// of [wrapped functions](crate::fns::FnWrapper) to the result expected by
279/// [`NativeFn`](crate::NativeFn).
280///
281/// Unlike with `TryInto` trait from the standard library, the erroneous result here does not
282/// mean that the conversion *itself* is impossible. Rather, it means that the function evaluation
283/// has failed for the provided args.
284///
285///
286/// This trait is implemented for base value types (such as [`Number`]s, [`Function`]s, [`Value`]s),
287/// for two container types: vectors and tuples, and for `Result`s with the error type
288/// convertible to [`ErrorOutput`].
289pub trait IntoEvalResult<T> {
290    /// Performs the conversion.
291    fn into_eval_result(self) -> Result<Value<T>, ErrorOutput>;
292}
293
294impl<T, U> IntoEvalResult<T> for Result<U, String>
295where
296    U: IntoEvalResult<T>,
297{
298    fn into_eval_result(self) -> Result<Value<T>, ErrorOutput> {
299        self.map_err(ErrorOutput::Message)
300            .and_then(U::into_eval_result)
301    }
302}
303
304impl<T, U> IntoEvalResult<T> for Result<U, Error>
305where
306    U: IntoEvalResult<T>,
307{
308    fn into_eval_result(self) -> Result<Value<T>, ErrorOutput> {
309        self.map_err(ErrorOutput::Spanned)
310            .and_then(U::into_eval_result)
311    }
312}
313
314impl<T: Number> IntoEvalResult<T> for T {
315    fn into_eval_result(self) -> Result<Value<T>, ErrorOutput> {
316        Ok(Value::Prim(self))
317    }
318}
319
320impl<T> IntoEvalResult<T> for () {
321    fn into_eval_result(self) -> Result<Value<T>, ErrorOutput> {
322        Ok(Value::void())
323    }
324}
325
326impl<T> IntoEvalResult<T> for bool {
327    fn into_eval_result(self) -> Result<Value<T>, ErrorOutput> {
328        Ok(Value::Bool(self))
329    }
330}
331
332impl<T> IntoEvalResult<T> for cmp::Ordering {
333    fn into_eval_result(self) -> Result<Value<T>, ErrorOutput> {
334        Ok(Value::opaque_ref(self))
335    }
336}
337
338impl<T> IntoEvalResult<T> for Value<T> {
339    fn into_eval_result(self) -> Result<Value<T>, ErrorOutput> {
340        Ok(self)
341    }
342}
343
344impl<T> IntoEvalResult<T> for Function<T> {
345    fn into_eval_result(self) -> Result<Value<T>, ErrorOutput> {
346        Ok(Value::Function(self))
347    }
348}
349
350impl<T> IntoEvalResult<T> for Tuple<T> {
351    fn into_eval_result(self) -> Result<Value<T>, ErrorOutput> {
352        Ok(Value::Tuple(self))
353    }
354}
355
356impl<T> IntoEvalResult<T> for Object<T> {
357    fn into_eval_result(self) -> Result<Value<T>, ErrorOutput> {
358        Ok(Value::Object(self))
359    }
360}
361
362impl<U, T> IntoEvalResult<T> for Vec<U>
363where
364    U: IntoEvalResult<T>,
365{
366    fn into_eval_result(self) -> Result<Value<T>, ErrorOutput> {
367        let values = self
368            .into_iter()
369            .map(U::into_eval_result)
370            .collect::<Result<Tuple<_>, _>>()?;
371        Ok(Value::Tuple(values))
372    }
373}
374
375macro_rules! into_value_for_tuple {
376    ($($i:tt : $ty:ident),+) => {
377        impl<Num, $($ty,)+> IntoEvalResult<Num> for ($($ty,)+)
378        where
379            $($ty: IntoEvalResult<Num>,)+
380        {
381            fn into_eval_result(self) -> Result<Value<Num>, ErrorOutput> {
382                Ok(Value::from(vec![$(self.$i.into_eval_result()?,)+]))
383            }
384        }
385    };
386}
387
388into_value_for_tuple!(0: T);
389into_value_for_tuple!(0: T, 1: U);
390into_value_for_tuple!(0: T, 1: U, 2: V);
391into_value_for_tuple!(0: T, 1: U, 2: V, 3: W);
392into_value_for_tuple!(0: T, 1: U, 2: V, 3: W, 4: X);
393into_value_for_tuple!(0: T, 1: U, 2: V, 3: W, 4: X, 5: Y);
394into_value_for_tuple!(0: T, 1: U, 2: V, 3: W, 4: X, 5: Y, 6: Z);
395into_value_for_tuple!(0: T, 1: U, 2: V, 3: W, 4: X, 5: Y, 6: Z, 7: A);
396into_value_for_tuple!(0: T, 1: U, 2: V, 3: W, 4: X, 5: Y, 6: Z, 7: A, 8: B);
397into_value_for_tuple!(0: T, 1: U, 2: V, 3: W, 4: X, 5: Y, 6: Z, 7: A, 8: B, 9: C);