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