Skip to main content

wolfram_library_link/
args.rs

1//! Traits for working with data types that can be passed natively via LibraryLink
2//! [`MArgument`]s.
3
4use std::{
5    cell::RefCell,
6    ffi::{CStr, CString},
7    os::raw::c_char,
8};
9
10use ref_cast::RefCast;
11
12#[cfg(feature = "wstp")]
13use crate::wstp::Link;
14use crate::{
15    expr::{expr, Expr, Symbol},
16    rtl,
17    sys::{self, mint, mreal, MArgument},
18    DataStore, Image, NumericArray,
19};
20
21/// Trait implemented for types that can be passed via an [`MArgument`].
22pub trait FromArg<'a> {
23    #[allow(missing_docs)]
24    unsafe fn from_arg(arg: &'a MArgument) -> Self;
25
26    /// Return the *LibraryLink* parameter type as a Wolfram Language expression.
27    ///
28    /// ```
29    /// use wolfram_library_link::{FromArg, NumericArray};
30    ///
31    /// assert_eq!(&bool::parameter_type().to_string(), "\"Boolean\"");
32    /// assert_eq!(&i64::parameter_type().to_string(), "System`Integer");
33    /// assert_eq!(
34    ///     &<&NumericArray<i8>>::parameter_type().to_string(),
35    ///     r#"System`List[System`LibraryDataType["NumericArray", "Integer8"], "Constant"]"#
36    /// );
37    /// ```
38    ///
39    /// See also [`IntoArg::return_type()`] and [`NativeFunction::signature()`].
40    fn parameter_type() -> Expr;
41}
42
43/// Trait implemented for types that can be returned via an [`MArgument`].
44///
45/// The [`MArgument`] that this trait is used to modify must be the return value of a
46/// LibraryLink function. It is not valid to modify [`MArgument`]s that contain
47/// LibraryLink function arguments.
48pub trait IntoArg {
49    /// Move `self` into `arg`.
50    ///
51    /// # Safety
52    ///
53    /// `arg` must be an uninitialized [`MArgument`] that is used to store the return
54    /// value of a LibraryLink function. The return type of that function must match
55    /// the type of `self.`
56    ///
57    /// This function must only be called immediately before returning from a LibraryLink
58    /// function. Each native LibraryLink function must perform at most one call to this
59    /// method per invocation.
60    //
61    // Private implementation note:
62    //   the "at most one call to this method per invocation" constraint is necessary to
63    //   maintain the safety invariants of `impl IntoArg for CString`.
64    unsafe fn into_arg(self, arg: MArgument);
65
66    /// Return the *LibraryLink* return type as a Wolfram Language expression.
67    ///
68    /// See also [`FromArg::parameter_type()`] and [`NativeFunction::signature()`].
69    fn return_type() -> Expr;
70}
71
72/// Trait implemented for any function whose parameters and return type are native
73/// LibraryLink [`MArgument`] types.
74///
75/// [`#[export]`][crate::export] can only be used with functions that implement this trait.
76///
77/// A function implements this trait if all of its parameters implement [`FromArg`] and
78/// its return type implements [`IntoArg`].
79///
80/// Functions that pass their arguments and return value using a [`wstp::Link`] do not
81/// implement this trait. See [`WstpFunction`].
82pub trait NativeFunction<'a> {
83    /// Call the function using the raw LibraryLink [`MArgument`] fields.
84    unsafe fn call(&self, args: &'a [MArgument], ret: MArgument);
85
86    /// Get the type signature of this function, suitable for use in
87    /// [`LibraryFunctionLoad`][ref/LibraryFunctionLoad]<code>[_, _, <i>parameters</i>, <i>ret</i>]</code>.
88    ///
89    /// [ref/LibraryFunctionLoad]: https://reference.wolfram.com/language/ref/LibraryFunctionLoad.html
90    ///
91    /// See also [`FromArg::parameter_type()`] and [`IntoArg::return_type()`].
92    ///
93    /// The function generated by [`generate_loader!`] uses this method to generate the
94    /// type signature for functions exported by [`#[export]`][crate::export].
95    // Note: This method takes `self` so that it is object safe.
96    fn signature(&self) -> Result<(Vec<Expr>, Expr), String>;
97}
98
99/// Trait implemented for any function whose parameters and return type can be passed
100/// over a WSTP [`Link`][crate::wstp::Link].
101///
102/// [`#[export(wstp)]`][crate::export#exportwstp] can only be used with functions that implement
103/// this trait.
104///
105/// A function implements this trait if its type signature is one of:
106///
107/// * `fn(_: &mut Link)`
108/// * `fn(_: Vec<Expr>) -> Expr`
109/// * `fn(_: Vec<Expr>)`
110#[cfg(feature = "wstp")]
111pub trait WstpFunction {
112    /// Call the function using the [`Link`] object passed by the Kernel.
113    unsafe fn call(&self, link: &mut Link);
114}
115
116//======================================
117// FromArg Impls
118//======================================
119
120impl FromArg<'_> for bool {
121    unsafe fn from_arg(arg: &MArgument) -> Self {
122        crate::bool_from_mbool(*arg.boolean)
123    }
124
125    fn parameter_type() -> Expr {
126        Expr::string("Boolean")
127    }
128}
129
130impl FromArg<'_> for mint {
131    unsafe fn from_arg(arg: &MArgument) -> Self {
132        *arg.integer
133    }
134
135    fn parameter_type() -> Expr {
136        expr!(System::Integer)
137    }
138}
139
140impl FromArg<'_> for mreal {
141    unsafe fn from_arg(arg: &MArgument) -> Self {
142        *arg.real
143    }
144
145    fn parameter_type() -> Expr {
146        expr!(System::Real)
147    }
148}
149
150impl FromArg<'_> for sys::mcomplex {
151    unsafe fn from_arg(arg: &MArgument) -> Self {
152        *arg.cmplex
153    }
154
155    fn parameter_type() -> Expr {
156        expr!(System::Complex)
157    }
158}
159
160//--------------------------------------
161// Strings
162//--------------------------------------
163
164unsafe fn c_str_from_arg<'a>(arg: &'a MArgument) -> &'a CStr {
165    let cstr: *mut c_char = *arg.utf8string;
166    CStr::from_ptr(cstr)
167}
168
169impl<'a> FromArg<'a> for CString {
170    unsafe fn from_arg(arg: &'a MArgument) -> CString {
171        let owned = {
172            let cstr: &'a CStr = c_str_from_arg(arg);
173            CString::from(cstr)
174        };
175
176        // Now that we own our own copy of the string, disown the Kernel's copy.
177        rtl::UTF8String_disown(*arg.utf8string);
178
179        owned
180    }
181
182    fn parameter_type() -> Expr {
183        expr!(System::String)
184    }
185}
186
187/// # Panics
188///
189/// This conversion will panic if the [`MArgument::utf8string`] field is not valid UTF-8.
190impl<'a> FromArg<'a> for String {
191    unsafe fn from_arg(arg: &'a MArgument) -> String {
192        let owned = {
193            let cstr: &'a CStr = c_str_from_arg(arg);
194            let str: &'a str = cstr
195                .to_str()
196                .expect("FromArg for &str: string was not valid UTF-8");
197            str.to_owned()
198        };
199
200        // Now that we own our own copy of the string, disown the Kernel's copy.
201        rtl::UTF8String_disown(*arg.utf8string);
202
203        owned
204    }
205
206    fn parameter_type() -> Expr {
207        expr!(System::String)
208    }
209}
210
211// TODO: Supported borrowed &CStr and &str's using some kind of wrapper that ensures we
212//       disown the Kernel string.
213
214/// # Safety
215///
216/// The lifetime of the returned `&CStr` must be the same as the lifetime of `arg`.
217///
218/// # Warning
219///
220/// Using `&CStr` as the parameter type of a *LibraryLink* function will result in a
221/// memory leak. Use [`String`] or [`CString`] instead.
222impl<'a> FromArg<'a> for &'a CStr {
223    unsafe fn from_arg(arg: &'a MArgument) -> &'a CStr {
224        c_str_from_arg(arg)
225    }
226
227    fn parameter_type() -> Expr {
228        // This type implements `FromArg` purely for usage in DataStoreNode::value()
229        // (via `FromArg for &str`).
230        panic!("&CStr cannot be used as a LibraryLink function parameter type")
231    }
232}
233
234/// # Panics
235///
236/// This conversion will panic if the [`MArgument::utf8string`] field is not valid UTF-8.
237///
238/// # Safety
239///
240/// The lifetime of the returned `&str` must be the same as the lifetime of `arg`.
241///
242/// # Warning
243///
244/// Using `&str` as the parameter type of a *LibraryLink* function will result in a
245/// memory leak. Use [`String`] or [`CString`] instead.
246impl<'a> FromArg<'a> for &'a str {
247    unsafe fn from_arg(arg: &'a MArgument) -> &'a str {
248        let cstr: &'a CStr = FromArg::<'a>::from_arg(arg);
249        cstr.to_str()
250            .expect("FromArg for &str: string was not valid UTF-8")
251    }
252
253    fn parameter_type() -> Expr {
254        // This type implements `FromArg` purely for usage in DataStoreNode::value().
255        panic!("&str cannot be used as a LibraryLink function parameter type")
256    }
257}
258
259//--------------------------------------
260// NumericArray
261//--------------------------------------
262
263// TODO: Add FromArg for NumericArray which just clones the numeric array? Or disclaims
264//       ownership in another way?
265
266/// # Safety
267///
268/// `FromArg for NumericArray<T>` MUST be constrained by `T: NumericArrayType` to prevent
269/// accidental creation of invalid `NumericArray` conversions. Without this constraint,
270/// it would be possible to write code like:
271///
272/// ```compile_fail
273/// # mod scope {
274/// # use wolfram_library_link::{export, NumericArray};
275/// #[export] // Unsafe!
276/// fn and(bools: NumericArray<bool>) -> bool {
277///     // ...
278/// #   todo!()
279/// }
280/// # }
281/// ```
282///
283/// which is not valid because `bool` is not a valid numeric array type.
284impl<'a, T: crate::NumericArrayType> FromArg<'a> for &'a NumericArray<T> {
285    unsafe fn from_arg(arg: &'a MArgument) -> &'a NumericArray<T> {
286        NumericArray::ref_cast(&*arg.numeric)
287    }
288
289    fn parameter_type() -> Expr {
290        // NOTE:
291        //   We use "Constant" instead of Automatic as the default memory management
292        //   strategy for &NumericArray<T> (and &Image<T> as well). This is because, for
293        //   both Automatic and "Constant", the fact remains that on the Rust side we have
294        //   an immutable reference to a NumericArray -- we're not going to free the array,
295        //   and we're not going to mutate. Using Automatic would behave correctly, but
296        //   would incur an unnecessary deep clone of the array contents as Automatic
297        //   doesn't imply any explicit promise from the programmer that they will not
298        //   mutate the array (unlike "Constant", which *is* an explicit promise that we
299        //   won't mutate the array the Kernel passes in).
300
301        // {LibraryDataType[NumericArray, "<T>"], "Constant"}
302        let type_name = T::TYPE.name();
303        let ldt = crate::expr::expr!(System::LibraryDataType["NumericArray", type_name]);
304        crate::expr::expr!(System::List[ldt, "Constant"])
305    }
306}
307
308impl<'a, T: crate::NumericArrayType> FromArg<'a> for NumericArray<T> {
309    unsafe fn from_arg(arg: &'a MArgument) -> NumericArray<T> {
310        NumericArray::from_raw(*arg.numeric)
311    }
312
313    fn parameter_type() -> Expr {
314        // {LibraryDataType[NumericArray, "<T>"], "Shared"}
315        let type_name = T::TYPE.name();
316        let ldt = crate::expr::expr!(System::LibraryDataType["NumericArray", type_name]);
317        crate::expr::expr!(System::List[ldt, "Shared"])
318    }
319}
320
321impl<'a> FromArg<'a> for &'a NumericArray<()> {
322    unsafe fn from_arg(arg: &'a MArgument) -> &'a NumericArray<()> {
323        NumericArray::ref_cast(&*arg.numeric)
324    }
325
326    fn parameter_type() -> Expr {
327        // {NumericArray, "Constant"}
328        crate::expr::expr!(System::List["NumericArray", "Constant"])
329    }
330}
331
332impl<'a> FromArg<'a> for NumericArray<()> {
333    unsafe fn from_arg(arg: &'a MArgument) -> NumericArray<()> {
334        NumericArray::from_raw(*arg.numeric)
335    }
336
337    fn parameter_type() -> Expr {
338        // {NumericArray, "Shared"}
339        crate::expr::expr!(System::List["NumericArray", "Shared"])
340    }
341}
342
343//--------------------------------------
344// Image
345//--------------------------------------
346
347impl<'a, T: crate::ImageData> FromArg<'a> for &'a Image<T> {
348    unsafe fn from_arg(arg: &'a MArgument) -> &'a Image<T> {
349        Image::ref_cast(&*arg.image)
350    }
351
352    fn parameter_type() -> Expr {
353        // {LibraryDataType[Image | Image3D, "<T>"], "Constant"}
354        let type_name = T::TYPE.name();
355        let alts = crate::expr::expr!(System::Alternatives["Image", "Image3D"]);
356        let ldt = crate::expr::expr!(System::LibraryDataType[alts, type_name]);
357        crate::expr::expr!(System::List[ldt, "Constant"])
358    }
359}
360
361impl<'a, T: crate::ImageData> FromArg<'a> for Image<T> {
362    unsafe fn from_arg(arg: &'a MArgument) -> Image<T> {
363        Image::from_raw(*arg.image)
364    }
365
366    fn parameter_type() -> Expr {
367        // {LibraryDataType[Image | Image3D, "<T>"], "Shared"}
368        let type_name = T::TYPE.name();
369        let alts = crate::expr::expr!(System::Alternatives["Image", "Image3D"]);
370        let ldt = crate::expr::expr!(System::LibraryDataType[alts, type_name]);
371        crate::expr::expr!(System::List[ldt, "Shared"])
372    }
373}
374
375impl<'a> FromArg<'a> for &'a Image<()> {
376    unsafe fn from_arg(arg: &'a MArgument) -> &'a Image<()> {
377        Image::ref_cast(&*arg.image)
378    }
379
380    fn parameter_type() -> Expr {
381        // {Image | Image3D, "Constant"}
382        let alts = crate::expr::expr!(System::Alternatives["Image", "Image3D"]);
383        crate::expr::expr!(System::List[alts, "Constant"])
384    }
385}
386
387impl<'a> FromArg<'a> for Image<()> {
388    unsafe fn from_arg(arg: &'a MArgument) -> Image<()> {
389        Image::from_raw(*arg.image)
390    }
391
392    fn parameter_type() -> Expr {
393        // {Image | Image3D, "Shared"}
394        let alts = crate::expr::expr!(System::Alternatives["Image", "Image3D"]);
395        crate::expr::expr!(System::List[alts, "Shared"])
396    }
397}
398
399//--------------------------------------
400// DataStore
401//--------------------------------------
402
403impl FromArg<'_> for DataStore {
404    unsafe fn from_arg(arg: &MArgument) -> DataStore {
405        DataStore::from_raw(*arg.tensor as sys::DataStore)
406    }
407
408    fn parameter_type() -> Expr {
409        Expr::string("DataStore")
410    }
411}
412
413impl<'a> FromArg<'a> for &'a DataStore {
414    unsafe fn from_arg(arg: &MArgument) -> &'a DataStore {
415        DataStore::ref_cast(&*(arg.tensor as *mut sys::DataStore))
416    }
417
418    fn parameter_type() -> Expr {
419        // This type implements `FromArg` purely for usage in DataStoreNode::value().
420        panic!("&DataStore cannot be used as a LibraryLink function parameter type")
421    }
422}
423
424//======================================
425// impl IntoArg
426//======================================
427
428impl IntoArg for () {
429    unsafe fn into_arg(self, _arg: MArgument) {
430        // Do nothing.
431    }
432
433    fn return_type() -> Expr {
434        Expr::string("Void")
435    }
436}
437
438//---------------------
439// Primitive data types
440//---------------------
441
442impl IntoArg for bool {
443    unsafe fn into_arg(self, arg: MArgument) {
444        let boole: u32 = if self { sys::True } else { sys::False };
445        *arg.boolean = boole as sys::mbool;
446    }
447
448    fn return_type() -> Expr {
449        Expr::string("Boolean")
450    }
451}
452
453impl IntoArg for mint {
454    unsafe fn into_arg(self, arg: MArgument) {
455        *arg.integer = self;
456    }
457
458    fn return_type() -> Expr {
459        expr!(System::Integer)
460    }
461}
462
463impl IntoArg for mreal {
464    unsafe fn into_arg(self, arg: MArgument) {
465        *arg.real = self;
466    }
467
468    fn return_type() -> Expr {
469        expr!(System::Real)
470    }
471}
472
473impl IntoArg for sys::mcomplex {
474    unsafe fn into_arg(self, arg: MArgument) {
475        *arg.cmplex = self;
476    }
477
478    fn return_type() -> Expr {
479        expr!(System::Complex)
480    }
481}
482
483//--------------------------------------------------
484// Convenience conversions for narrow integer sizes.
485//--------------------------------------------------
486
487impl IntoArg for i8 {
488    unsafe fn into_arg(self, arg: MArgument) {
489        *arg.integer = mint::from(self);
490    }
491
492    fn return_type() -> Expr {
493        expr!(System::Integer)
494    }
495}
496
497impl IntoArg for i16 {
498    unsafe fn into_arg(self, arg: MArgument) {
499        *arg.integer = mint::from(self);
500    }
501
502    fn return_type() -> Expr {
503        expr!(System::Integer)
504    }
505}
506
507impl IntoArg for i32 {
508    unsafe fn into_arg(self, arg: MArgument) {
509        *arg.integer = mint::from(self);
510    }
511
512    fn return_type() -> Expr {
513        expr!(System::Integer)
514    }
515}
516
517impl IntoArg for u8 {
518    unsafe fn into_arg(self, arg: MArgument) {
519        *arg.integer = mint::from(self);
520    }
521
522    fn return_type() -> Expr {
523        expr!(System::Integer)
524    }
525}
526
527impl IntoArg for u16 {
528    unsafe fn into_arg(self, arg: MArgument) {
529        *arg.integer = mint::from(self);
530    }
531
532    fn return_type() -> Expr {
533        expr!(System::Integer)
534    }
535}
536
537// If we're on a 32 bit platform, mint might be an alias for i32. Avoid providing this
538// conversion on those platforms.
539#[cfg(target_pointer_width = "64")]
540impl IntoArg for u32 {
541    unsafe fn into_arg(self, arg: MArgument) {
542        *arg.integer = mint::from(self);
543    }
544
545    fn return_type() -> Expr {
546        expr!(System::Integer)
547    }
548}
549
550//--------------------
551// Strings
552//--------------------
553
554thread_local! {
555    /// See [`<CString as IntoArg>::into_arg()`] for information about how this static is
556    /// used.
557    static RETURNED_STRING: RefCell<Option<CString>> = RefCell::new(None);
558}
559
560impl IntoArg for CString {
561    unsafe fn into_arg(self, arg: MArgument) {
562        // Extend the lifetime of `self.as_ptr()` by storing `self` in `RETURNED_STRING`.
563        //
564        // This will keep `raw` valid past the point that the current LibraryLink
565        // function returns, at which point it will be copied by the Kernel and is no
566        // longer used. We'll drop `self` the next time this function is called.
567        //
568        // This implementation limits the maximum number of "leaked" strings to just one.
569        //
570        // For more information on management of string memory when passed via
571        // LibraryLink, see:
572        //
573        // <https://reference.wolfram.com/language/LibraryLink/tutorial/InteractionWithWolframLanguage.html#262826222>
574        let raw: *const c_char = RETURNED_STRING.with(|stored| {
575            // Drop the previously returned string, if any.
576            if let Some(prev) = stored.replace(None) {
577                drop(prev);
578            }
579
580            let raw: *const c_char = self.as_ptr();
581
582            *stored.borrow_mut() = Some(self);
583
584            raw
585        });
586
587        // Return `raw` via this MArgument.
588        *arg.utf8string = raw as *mut c_char;
589    }
590
591    fn return_type() -> Expr {
592        expr!(System::String)
593    }
594}
595
596impl IntoArg for String {
597    /// # Panics
598    ///
599    /// This function will panic if `self` cannot be converted into a [`CString`].
600    unsafe fn into_arg(self, arg: MArgument) {
601        let cstring = CString::new(self)
602            .expect("IntoArg for String: could not convert String to CString");
603
604        <CString as IntoArg>::into_arg(cstring, arg)
605    }
606
607    fn return_type() -> Expr {
608        expr!(System::String)
609    }
610}
611
612//---------------------------------------
613// NumericArray, Image, DataStore
614//---------------------------------------
615
616impl<T: crate::NumericArrayType> IntoArg for NumericArray<T> {
617    unsafe fn into_arg(self, arg: MArgument) {
618        *arg.numeric = self.into_raw();
619    }
620
621    fn return_type() -> Expr {
622        // LibraryDataType[NumericArray, "<T>"]
623        let type_name = T::TYPE.name();
624        crate::expr::expr!(System::LibraryDataType["NumericArray", type_name])
625    }
626}
627
628impl IntoArg for NumericArray<()> {
629    unsafe fn into_arg(self, arg: MArgument) {
630        *arg.numeric = self.into_raw();
631    }
632
633    fn return_type() -> Expr {
634        // NumericArray
635        crate::expr::expr!("NumericArray")
636    }
637}
638
639impl<T: crate::ImageData> IntoArg for Image<T> {
640    unsafe fn into_arg(self, arg: MArgument) {
641        *arg.image = self.into_raw();
642    }
643
644    fn return_type() -> Expr {
645        // LibraryDataType[Image | Image3D, "<T>"]
646        let type_name = T::TYPE.name();
647        let alts = crate::expr::expr!(System::Alternatives["Image", "Image3D"]);
648        let ldt = crate::expr::expr!(System::LibraryDataType[alts, type_name]);
649        crate::expr::expr!(System::List[ldt, "Shared"])
650    }
651}
652
653impl IntoArg for DataStore {
654    unsafe fn into_arg(self, arg: MArgument) {
655        *arg.tensor = self.into_raw() as *mut _;
656    }
657
658    fn return_type() -> Expr {
659        Expr::string("DataStore")
660    }
661}
662
663//======================================
664// impl NativeFunction
665//======================================
666
667/// Implement `NativeFunction` for functions that use raw [`MArgument`]s for their
668/// arguments and return value.
669///
670/// # Example
671///
672/// ```
673/// # mod scope {
674/// use wolfram_library_link::{self as wll, sys::MArgument, FromArg};
675///
676/// #[wll::export]
677/// fn raw_add2(args: &[MArgument], ret: MArgument) {
678///     let x = unsafe { i64::from_arg(&args[0]) };
679///     let y = unsafe { i64::from_arg(&args[1]) };
680///
681///     unsafe {
682///         *ret.integer = x + y;
683///     }
684/// }
685/// # }
686/// ```
687///
688/// ```wolfram
689/// LibraryFunctionLoad["...", "raw_add2", {Integer, Integer}, Integer]
690/// ```
691impl<'a: 'b, 'b> NativeFunction<'a> for fn(&'b [MArgument], MArgument) {
692    unsafe fn call(&self, args: &'a [MArgument], ret: MArgument) {
693        self(args, ret)
694    }
695
696    fn signature(&self) -> Result<(Vec<Expr>, Expr), String> {
697        Err(
698            "fn(&[MArgument], MArgument) function cannot be loaded automatically: \
699            parameter and return types are unknown."
700                .to_owned(),
701        )
702    }
703}
704
705//--------------------
706// impl NativeFunction
707//--------------------
708
709macro_rules! impl_NativeFunction {
710    ($($type:ident),*) => {
711        impl<'a, $($type,)* R> NativeFunction<'a> for fn($($type),*) -> R
712        where
713            R: IntoArg,
714            $($type: FromArg<'a>),*
715        {
716            unsafe fn call(&self, args: &'a [MArgument], ret: MArgument) {
717                // Re-use the $type name as the local variable names. E.g.
718                //     let A1 = A1::from_arg(..);
719                // This works because types and variable names are different namespaces.
720                #[allow(non_snake_case)]
721                let [$($type,)*] = match args {
722                    [$($type,)*] => [$($type,)*],
723                    _ => panic!(
724                        "LibraryLink function number of arguments ({}) does not match \
725                        number of parameters",
726                        args.len()
727                    ),
728                };
729
730                $(
731                    #[allow(non_snake_case)]
732                    let $type: $type = $type::from_arg($type);
733                )*
734
735                let result: R = self($($type,)*);
736
737                result.into_arg(ret);
738            }
739
740            fn signature(&self) -> Result<(Vec<Expr>, Expr), String> {
741                let mut param_tys = Vec::new();
742
743                $(
744                    param_tys.push($type::parameter_type());
745                )*
746
747                Ok((param_tys, R::return_type()))
748            }
749        }
750    }
751}
752
753// Handle the zero-arguments case specially.
754impl<'a, R> NativeFunction<'a> for fn() -> R
755where
756    R: IntoArg,
757{
758    unsafe fn call(&self, args: &[MArgument], ret: MArgument) {
759        if args.len() != 0 {
760            panic!(
761                "LibraryLink function number of arguments ({}) does not match number of \
762                parameters",
763                args.len()
764            );
765        }
766
767        let result = self();
768
769        result.into_arg(ret);
770    }
771
772    fn signature(&self) -> Result<(Vec<Expr>, Expr), String> {
773        Ok((Vec::new(), R::return_type()))
774    }
775}
776
777impl_NativeFunction!(A1);
778impl_NativeFunction!(A1, A2);
779impl_NativeFunction!(A1, A2, A3);
780impl_NativeFunction!(A1, A2, A3, A4);
781impl_NativeFunction!(A1, A2, A3, A4, A5);
782impl_NativeFunction!(A1, A2, A3, A4, A5, A6);
783impl_NativeFunction!(A1, A2, A3, A4, A5, A6, A7);
784impl_NativeFunction!(A1, A2, A3, A4, A5, A6, A7, A8);
785impl_NativeFunction!(A1, A2, A3, A4, A5, A6, A7, A8, A9);
786impl_NativeFunction!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
787impl_NativeFunction!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);
788impl_NativeFunction!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12);
789
790//======================================
791// impl WstpFunction
792//======================================
793
794// Everything from this point on is WSTP-specific: trait impls + helper fns
795// that read/write Expr values through a WSTP `Link`.
796#[cfg(feature = "wstp")]
797mod wstp_impls {
798    use super::*;
799
800    /// Implement [`WstpFunction`] for functions that use a [`Link`] for their arguments and
801    /// return value.
802    ///
803    /// # Example
804    ///
805    /// ```
806    /// # mod scope {
807    /// use wolfram_library_link::{self as wll, wstp::Link};
808    ///
809    /// #[wll::export(wstp)]
810    /// fn add2_link(link: &mut Link) {
811    ///     let argc: usize = link.test_head("List").unwrap();
812    ///
813    ///     if argc != 2 {
814    ///         panic!("expected 2 arguments, got {}", argc);
815    ///     }
816    ///
817    ///     let x = link.get_i64().unwrap();
818    ///     let y = link.get_i64().unwrap();
819    ///
820    ///     link.put_i64(x + y).unwrap();
821    /// }
822    /// # }
823    /// ```
824    ///
825    /// ```wolfram
826    /// LibraryFunctionLoad["...", "add2_link", LinkObject, LinkObject]
827    /// ```
828    impl WstpFunction for fn(&mut Link) {
829        unsafe fn call(&self, link: &mut Link) {
830            self(link)
831        }
832    }
833
834    /// Implement [`WstpFunction`] for functions that use [`Expr`] for their arguments and
835    /// return value.
836    ///
837    /// # Example
838    ///
839    /// ```
840    /// # mod scope {
841    /// use wolfram_library_link::{self as wll, wstp::Link, expr::{Expr, ExprKind}};
842    ///
843    /// #[wll::export(wstp)]
844    /// fn add2(args: Vec<Expr>) -> Expr {
845    ///     if args.len() != 2 {
846    ///         panic!("expected 2 arguments, got {}", args.len());
847    ///     }
848    ///
849    ///     let x: i64 = match *args[0].kind() {
850    ///         ExprKind::Integer(value) => value,
851    ///         _ => panic!("expected 1st argument to be Integer, got: {}", args[0])
852    ///     };
853    ///     let y: i64 = match *args[1].kind() {
854    ///         ExprKind::Integer(value) => value,
855    ///         _ => panic!("expected 2nd argument to be Integer, got: {}", args[1])
856    ///     };
857    ///
858    ///     Expr::from(x + y)
859    /// }
860    /// # }
861    /// ```
862    ///
863    /// ```wolfram
864    /// LibraryFunctionLoad["...", "add2", LinkObject, LinkObject]
865    /// ```
866    impl WstpFunction for fn(Vec<Expr>) -> Expr {
867        unsafe fn call(&self, link: &mut Link) {
868            let args: Vec<Expr> = match get_args_list(link) {
869                Ok(args) => args,
870                Err(err) => return write_arg_failure(link, err),
871            };
872
873            let result: Expr = self(args);
874
875            if let Err(err) = link.put_expr(&result) {
876                write_arg_failure(link, err.into());
877            }
878        }
879    }
880
881    impl WstpFunction for fn(Vec<Expr>) {
882        unsafe fn call(&self, link: &mut Link) {
883            let args: Vec<Expr> = match get_args_list(link) {
884                Ok(args) => args,
885                Err(err) => return write_arg_failure(link, err),
886            };
887
888            let _null: () = self(args);
889
890            if let Err(err) = link.put_symbol("System`Null") {
891                write_arg_failure(link, err.into());
892            }
893        }
894    }
895
896    /// Write a structured `Failure[...]` to the link for an argument read /
897    /// result write failure, instead of panicking into a generic RustPanic.
898    fn write_arg_failure(link: &mut Link, err: crate::LibraryError) {
899        let _ = crate::macro_utils::write_failure_to_link(link, &err);
900    }
901
902    //----------------------------
903    // Utilities
904    //----------------------------
905
906    fn get_args_list(link: &mut Link) -> Result<Vec<Expr>, crate::LibraryError> {
907        Ok(get_args_list_impl(link)?)
908    }
909
910    fn get_args_list_impl(link: &mut Link) -> Result<Vec<Expr>, wstp::Error> {
911        let arg_count: usize = match link.test_head("List") {
912            Ok(count) => Ok(count),
913            Err(err) if err.code() == Some(wstp::sys::WSEGSEQ) => {
914                link.clear_error();
915                link.test_head("System`List")
916            },
917            Err(err) => Err(err),
918        }?;
919
920        let mut elements: Vec<Expr> = Vec::new();
921
922        for _ in 0..arg_count {
923            let elem = link.get_expr_with_resolver(&mut |name| {
924                Symbol::try_new(&format!("System`{name}"))
925            })?;
926            elements.push(elem);
927        }
928
929        Ok(elements)
930    }
931} // end of #[cfg(feature = "wstp")] mod wstp_impls