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