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