wll/
adaptor.rs

1//! Some adaptor interface for Wolfram LibraryLink.
2
3use crate::errors::{Error, ErrorKind};
4use crate::Result;
5use std::convert::TryInto;
6use std::num::Wrapping;
7use sys::{mbool, mcomplex, mint, mreal, MArgument, MImage, MNumericArray, MSparseArray, MTensor};
8
9mod private {
10    pub trait Sealed {}
11}
12
13/// Basic trait for Wolfram LibraryLink underlying type.
14/// Typically doesn’t need to be used directly.
15///
16/// You **CANNOT** implement it outside.
17pub trait MType: private::Sealed + Sized {}
18
19macro_rules! impl_mtypes {
20    ($($t:ty),+) => {
21        $(
22            impl private::Sealed for $t {}
23            impl MType for $t {}
24        )+
25    };
26}
27
28impl_mtypes!(
29    mbool,
30    mint,
31    mreal,
32    mcomplex,
33    MTensor,
34    MSparseArray,
35    MNumericArray,
36    MImage
37);
38
39// `MType` or `()`.
40#[doc(hidden)]
41pub trait MTypeOrVoid: private::Sealed {}
42
43impl<T: MType> MTypeOrVoid for T {}
44impl private::Sealed for () {}
45impl MTypeOrVoid for () {}
46
47/// Adaptor for [`MType`] input.
48///
49/// [`MType`]: ./trait.MType.html
50pub trait InputAdaptor: Sized {
51    /// Input type.
52    type Input: MType;
53
54    /// Performs the conversion.
55    fn mtype_try_from(input: Self::Input) -> Result<Self>;
56}
57
58/// Adaptor for [`MType`] output.
59///
60/// [`MType`]: ./trait.MType.html
61pub trait OutputAdaptor: Sized {
62    /// Output type.
63    type Output: MType;
64
65    /// Performs the conversion.
66    fn try_into_mtype(self) -> Result<Self::Output>;
67}
68
69// Adaptor trait for getting `MArgument`.
70// Typically doesn’t need to be used directly.
71//
72// `MArgumentGetter<T>` will be implemented automatically if proper `InputAdaptor` has been implemented.
73//
74// **DO NOT** implement this trait yourself.
75#[doc(hidden)]
76pub trait MArgumentGetter<T: MType>: Sized {
77    /// Try to get `MArgument`.
78    fn try_get_arg(arg: MArgument) -> Result<Self>;
79}
80
81// Adaptor trait for setting `MArgument`.
82// Typically doesn’t need to be used directly.
83//
84// `MArgumentSetter<T>` will be implemented automatically if proper `OutputAdaptor` has been implemented.
85//
86// **DO NOT** implement this trait yourself.
87#[doc(hidden)]
88pub trait MArgumentSetter<T: MTypeOrVoid>: Sized {
89    /// Try to set `MArgument`.
90    fn try_set_arg(self, arg: &MArgument) -> Result<()>;
91}
92
93impl MArgumentSetter<()> for () {
94    #[inline]
95    fn try_set_arg(self, _arg: &MArgument) -> Result<()> {
96        Ok(())
97    }
98}
99
100macro_rules! impl_argument_getter {
101    ($fd:ident: $t:ty) => {
102        impl<T: InputAdaptor<Input = $t>> MArgumentGetter<$t> for T {
103            #[inline]
104            fn try_get_arg(arg: MArgument) -> Result<Self> {
105                unsafe {
106                    let ptr = arg.$fd;
107                    if ptr.is_null() {
108                        return Err(Error::from(ErrorKind::TypeError));
109                    }
110                    T::mtype_try_from(std::ptr::read(ptr))
111                }
112            }
113        }
114    };
115}
116
117macro_rules! impl_argument_setter {
118    ($fd:ident: $t:ty) => {
119        impl<T: OutputAdaptor<Output = $t>> MArgumentSetter<$t> for T {
120            #[inline]
121            fn try_set_arg(self, arg: &MArgument) -> Result<()> {
122                unsafe {
123                    let ptr = arg.$fd;
124                    if ptr.is_null() {
125                        return Err(Error::from(ErrorKind::TypeError));
126                    }
127                    std::ptr::write(ptr, self.try_into_mtype()?);
128                }
129                Ok(())
130            }
131        }
132    };
133}
134
135macro_rules! impl_argument_getter_setter {
136    {$($fd:ident: $t:ty,)*} => {
137        $(
138            impl_argument_getter!($fd: $t);
139            impl_argument_setter!($fd: $t);
140        )*
141    };
142}
143
144impl_argument_getter_setter! {
145    boolean: mbool,
146    integer: mint,
147    real: mreal,
148    cmplex: mcomplex,
149    tensor: MTensor,
150    sparse: MSparseArray,
151    numeric: MNumericArray,
152    image: MImage,
153}
154
155impl InputAdaptor for bool {
156    type Input = mbool;
157
158    #[inline]
159    fn mtype_try_from(input: Self::Input) -> Result<Self> {
160        Ok(input != sys::False)
161    }
162}
163
164impl OutputAdaptor for bool {
165    type Output = mbool;
166
167    #[inline]
168    fn try_into_mtype(self) -> Result<Self::Output> {
169        Ok(if self { sys::True } else { sys::False })
170    }
171}
172
173macro_rules! impl_int_adaptor {
174    ($($t:ty),+) => {
175        $(
176            impl InputAdaptor for $t {
177                type Input = mint;
178
179                #[inline]
180                fn mtype_try_from(input: Self::Input) -> Result<Self> {
181                    input
182                        .try_into()
183                        .map_err(|_| Error::from(ErrorKind::TypeError))
184                }
185            }
186            impl OutputAdaptor for $t {
187                type Output = mint;
188
189                #[inline]
190                fn try_into_mtype(self) -> Result<Self::Output> {
191                    self.try_into()
192                        .map_err(|_| Error::from(ErrorKind::TypeError))
193                }
194            }
195        )+
196    }
197}
198
199impl_int_adaptor!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize);
200
201macro_rules! impl_wrapping_int_adaptor {
202    ($($t:ty),+) => {
203        $(
204            impl InputAdaptor for Wrapping<$t> {
205                type Input = mint;
206
207                #[inline]
208                fn mtype_try_from(input: Self::Input) -> Result<Self> {
209                    Ok(Wrapping(<$t>::mtype_try_from(input)?))
210                }
211            }
212            impl OutputAdaptor for Wrapping<$t> {
213                type Output = mint;
214
215                #[inline]
216                fn try_into_mtype(self) -> Result<Self::Output> {
217                    self.0.try_into_mtype()
218                }
219            }
220        )+
221    };
222}
223
224impl_wrapping_int_adaptor!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize);
225
226macro_rules! impl_real_adaptor {
227    ($($t:ty),+) => {
228        $(
229            impl InputAdaptor for $t {
230                type Input = mreal;
231
232                fn mtype_try_from(input: Self::Input) -> Result<Self> {
233                    Ok(input as Self)
234                }
235            }
236            impl OutputAdaptor for $t {
237                type Output = mreal;
238
239                fn try_into_mtype(self) -> Result<Self::Output> {
240                    Ok(self as Self::Output)
241                }
242            }
243        )+
244    };
245}
246
247impl_real_adaptor!(f32, f64);
248
249#[cfg(test)]
250mod tests {
251    use super::*;
252    use std::mem::MaybeUninit;
253    use sys::{mbool, MArgument};
254
255    #[test]
256    fn bool_true_input() {
257        assert_eq!(bool::mtype_try_from(sys::True), Ok(true));
258    }
259
260    #[test]
261    fn bool_false_input() {
262        assert_eq!(bool::mtype_try_from(sys::False), Ok(false));
263    }
264
265    #[test]
266    fn bool_true_output() {
267        assert_eq!(true.try_into_mtype(), Ok(sys::True));
268    }
269
270    #[test]
271    fn bool_false_output() {
272        assert_eq!(false.try_into_mtype(), Ok(sys::False));
273    }
274
275    #[test]
276    fn bool_true_get() {
277        let mut mb = sys::True;
278        let arg = MArgument { boolean: &mut mb };
279        assert_eq!(bool::try_get_arg(arg), Ok(true));
280    }
281
282    #[test]
283    fn bool_false_get() {
284        let mut mb = sys::False;
285        let arg = MArgument { boolean: &mut mb };
286        assert_eq!(bool::try_get_arg(arg), Ok(false));
287    }
288
289    #[test]
290    fn bool_true_set() {
291        let mut mb = MaybeUninit::<mbool>::uninit();
292        let arg = MArgument {
293            boolean: mb.as_mut_ptr(),
294        };
295        let res = true.try_set_arg(&arg);
296        let mb = unsafe { mb.assume_init() };
297        assert_eq!((mb, res), (sys::True, Ok(())));
298    }
299
300    #[test]
301    fn bool_false_set() {
302        let mut mb = MaybeUninit::<mbool>::uninit();
303        let arg = MArgument {
304            boolean: mb.as_mut_ptr(),
305        };
306        let res = false.try_set_arg(&arg);
307        let mb = unsafe { mb.assume_init() };
308        assert_eq!((mb, res), (sys::False, Ok(())));
309    }
310}