wolfram_library_link/lib.rs
1//! A safe and ergonomic wrapper around Wolfram [LibraryLink][library-link-guide].
2//!
3//! Wolfram LibraryLink is an interface for dynamic libraries that can be
4//! [dynamically loaded][LibraryFunctionLoad] by the [Wolfram Language][WL]. This crate
5//! provides idiomatic Rust bindings around the lower-level LibraryLink C interface.
6//!
7//! **Features:**
8//!
9//! * Call Rust functions from Wolfram code.
10//! * Pass data efficiently between Rust and Wolfram code using native data types
11//! like [`NumericArray`] and [`Image`].
12//! * Pass arbitrary expressions between Rust and Wolfram code using
13//! [`Expr`][struct@crate::Expr] and the [`#[export(wstp)]`][crate::export#exportwstp]
14//! macro.
15//! * Generate asynchronous events handled by the Wolfram Language, using an [`AsyncTaskObject`]
16//! background thread.
17//!
18//!
19//!
20//!
21//! # What is Wolfram LibraryLink?
22//!
23//! > Wolfram LibraryLink provides a powerful way to connect external code to the Wolfram
24//! > Language, enabling high-speed and memory-efficient execution. It does this by
25//! > allowing dynamic libraries to be directly loaded into the Wolfram Language kernel so
26//! > that functions in the libraries can be immediately called from the Wolfram Language.
27//! >
28//! > — [Wolfram LibraryLink User Guide](https://reference.wolfram.com/language/LibraryLink/tutorial/Overview.html)
29//!
30//! ## Library function types
31//!
32//! The Wolfram LibraryLink interface supports two different types of library function:
33//!
34//! * Native functions
35//! * WSTP functions
36//!
37//! The two function types differ in how their arguments and return value are passed
38//! between the Wolfram Language and the compiled library function.
39//!
40//! Native functions pass their values using efficient native data types, like machine sized
41//! integers, floating point numbers, C strings, or [`NumericArray`]s. Calling a native
42//! function is efficient, but they are unable to pass more complicated values
43//! (like general Wolfram Language expressions).
44//!
45//! WSTP functions pass their values using WSTP [`Link`] objects. A `Link` object can
46//! be used to pass arbitrary Wolfram Language expressions to or from the library
47//! function. However, WSTP links are less efficient for passing basic numeric arguments.
48//!
49//!
50//!
51//!
52//! # Examples
53//!
54//! **Example programs:** In addition to the basic example programs show below, the
55//! wolfram-library-link repository contains
56//! [example programs](https://github.com/WolframResearch/wolfram-library-link-rs#example-programs)
57//! demonstrating the major features of this crate.
58//!
59//! **Quick Start**: The [LibraryLink for Rust Quick Start][QuickStart] is a complete tutorial
60//! covering how to create a new Rust library, compile it, and call into it from the
61//! Wolfram Language.
62//!
63//! ## Native functions
64//!
65//! Rust functions can be exported for use from the Wolfram Language using the
66//! [`#[export]`][crate::export] macro:
67//!
68//! ```
69//! # mod scope {
70//! use wolfram_library_link::export;
71//!
72//! #[export]
73//! fn square(x: i64) -> i64 {
74//! x * x
75//! }
76//! # }
77//! ```
78//!
79//! Then, after building the Rust code into a dynamic library, the function can be loaded
80//! into the Wolfram Language using [`LibraryFunctionLoad`][LibraryFunctionLoad]:
81//!
82//! ```wolfram
83//! square = LibraryFunctionLoad["<library name>", "square", {Integer}, Integer];
84//! ```
85//!
86//! After being loaded, a library function can be called like any other Wolfram Language
87//! function:
88//!
89//! ```wolfram
90//! square[5] (* Returns 25 *)
91//! ```
92//!
93//! [QuickStart]: https://github.com/WolframResearch/wolfram-library-link-rs/blob/master/docs/QuickStart.md
94//!
95//! ## WSTP Functions
96//!
97//! Rust WSTP functions can be exported for use from the Wolfram Language using the
98//! [`#[export(wstp)]`][crate::export#exportwstp] macro:
99//!
100//! ```
101//! # mod scope {
102//! use wolfram_library_link::{export, wstp::Link};
103//!
104//! #[export(wstp)]
105//! fn square(link: &mut Link) {
106//! // WSTP function arguments are passed as a List expression: {...}
107//! let arg_count = link.test_head("System`List").unwrap();
108//!
109//! // Get that the argument list contains a single element.
110//! if arg_count != 1 {
111//! panic!("square: expected one argument")
112//! }
113//!
114//! // Check that the argument is an integer, and get it's value.
115//! let x: i64 = link.get_i64().expect("expected Integer argument");
116//!
117//! // Write the return value.
118//! link.put_i64(x * x).unwrap();
119//! }
120//! # }
121//! ```
122//!
123//!
124//!
125//!
126//! # Documentation
127//!
128//! * [How To: Convert Between Rust and Wolfram Types][crate::docs::converting_between_rust_and_wolfram_types]
129//! * [How To: Evaluate Wolfram code from Rust][docs::evaluate_wolfram_code_from_rust]
130//!
131//! *See the [`docs`] module for a complete list of available long-form documentation.*
132//!
133//! <br />
134//!
135//!
136//!
137//! # Additional Features
138//!
139//! ### Non-primitive native types
140//!
141//! Native functions support passing primitive types like [`bool`], [`i64`], [`f64`],
142//! and strings. Additionally, they also support a small number of non-primitive types
143//! that can be used to efficiently pass more complicated data structures between the
144//! Wolfram Langauge and compiled code, without requiring the full generality of using a
145//! WSTP function.
146//!
147//! The set of currently supported non-primitive native types includes:
148//!
149//! * [`NumericArray`]
150//! * [`Image`]
151//! * [`DataStore`]
152//!
153//! ### Cooperative computation abort handling
154//!
155//! The Wolfram Language supports the ability for the user to abort an in-progress
156//! computation, without ending the process and losing their current state. This is
157//! accomplished by code that checks if an abort has been triggered — and if so, performs an early return
158//! — which is placed at important points in the Wolfram Language kernel
159//! evaluation process.
160//!
161//! User libraries can cooperatively include abort checking logic in their library using
162//! the [`aborted()`] function. This enables LibraryLink libraries to provide the same
163//! user experience as built in Wolfram Language functions. LibraryLink libraries that may
164//! perform long computations are especially encouraged to do abort checking within loops
165//! that may run for a long time.
166//!
167//! ```no_run
168//! use wolfram_library_link as wll;
169//!
170//! if wll::aborted() {
171//! // The user aborted this computation, so it doesn't matter what we return.
172//! panic!("Wolfram abort");
173//! }
174//! ```
175//!
176//! *Note: The Wolfram Language 'abort' command is not at all related to the
177//! C/Rust [`abort()`][std::process::abort] function. The `abort()` function is typically used
178//! when an unrecoverable error occurs, at a point determined by the programmer. The
179//! Wolfram 'abort' command can be issued by the user at any point, and is commonly used
180//! to end long-running computations the user no longer wishes to wait for.*
181//!
182//! ### Show backtrace when a panic occurs
183//!
184//! [WSTP functions](#wstp-functions) will automatically catch any
185//! Rust panics that occur in the wrapped code, and return a [`Failure`][failure] object
186//! with the panic message and source file/line number. The failure can optionally include
187//! a backtrace showing the location of the panic.
188//!
189//! This is configured by the `"LIBRARY_LINK_RUST_BACKTRACE"` environment variable. Enable
190//! it by evaluating:
191//!
192//! ```wolfram
193//! SetEnvironment["LIBRARY_LINK_RUST_BACKTRACE" -> "True"]
194//! ```
195//!
196//! Now the error shown when a panic occurs will include a backtrace.
197//!
198//! *The error message may include more information if the `"nightly"`
199//! [feature][cargo-features] of `wolfram-library-link` is enabled.*
200//!
201//!
202//!
203//!
204//! # Related Links
205//!
206//! * [LibraryLink for Rust Quick Start][QuickStart]
207//! * [Wolfram LibraryLink User Guide](https://reference.wolfram.com/language/LibraryLink/tutorial/Overview.html)
208//!
209//!
210//!
211//!
212//! [WL]: https://wolfram.com/language
213//! [library-link-guide]: https://reference.wolfram.com/language/guide/LibraryLink.html
214//! [LibraryFunctionLoad]: https://reference.wolfram.com/language/ref/LibraryFunctionLoad.html
215//! [failure]: https://reference.wolfram.com/language/ref/Failure.html
216//! [cargo-features]: https://doc.rust-lang.org/cargo/reference/features.html
217// #![doc = include_str!("../docs/included/Overview.md")]
218#![cfg_attr(feature = "nightly", feature(panic_info_message))]
219#![warn(missing_docs)]
220
221mod args;
222mod async_tasks;
223mod catch_panic;
224mod data_store;
225mod image;
226mod library_data;
227mod numeric_array;
228
229/// This module is *semver exempt*. This is not intended to be part of the public API of
230/// wolfram-library-link.
231///
232/// Utilities used by code generated by the public macros.
233#[doc(hidden)]
234pub mod macro_utils;
235pub mod managed;
236pub mod rtl;
237
238pub mod docs;
239
240
241// Note: This is exported as doc(inline) so that it shows up in the 'Modules' section of
242// the crate docs instead of in the 'Re-exports' section. This is to make way for
243// the chance that in the future, wolfram-library-link will have it's own expression
244// type that uses types like NumericArray and Image as variants, which can't be
245// used in the more general wolfram-expr crate (since NumericArray and Image depend
246// on the Wolfram RTL, which isn't available in arbitrary Rust code).
247#[doc(inline)]
248pub use wolfram_expr as expr;
249pub use wolfram_library_link_sys as sys;
250pub use wstp;
251
252
253// Used by the #[export]/#[export(wstp)] macro implementations.
254#[cfg(feature = "automate-function-loading-boilerplate")]
255#[doc(hidden)]
256pub use inventory;
257
258#[cfg(feature = "automate-function-loading-boilerplate")]
259pub use self::macro_utils::exported_library_functions_association;
260
261
262pub use self::{
263 args::{FromArg, IntoArg, NativeFunction, WstpFunction},
264 async_tasks::AsyncTaskObject,
265 data_store::{DataStore, DataStoreNode, DataStoreNodeValue, Nodes},
266 image::{ColorSpace, Image, ImageData, ImageType, Pixel, UninitImage},
267 library_data::{get_library_data, initialize, WolframLibraryData},
268 numeric_array::{
269 NumericArray, NumericArrayConvertMethod, NumericArrayDataType, NumericArrayKind,
270 NumericArrayType, UninitNumericArray,
271 },
272};
273
274
275
276use std::sync::Mutex;
277
278use once_cell::sync::Lazy;
279
280use wolfram_library_link_sys::mint;
281use wstp::Link;
282
283pub(crate) use self::library_data::assert_main_thread;
284use crate::expr::{Expr, ExprKind, Symbol};
285
286//--------------------------------------
287// Re-exported items
288//--------------------------------------
289
290/// Designate an initialization function to run when this library is loaded via Wolfram
291/// LibraryLink.
292///
293/// `#[init]` can be applied to at most one function in a library.
294///
295/// The function annotated with `#[init]` will automatically call [`initialize()`].
296///
297/// LibraryLink libraries are not required to define an initialization function.
298///
299/// # Panics
300///
301/// Any panics thrown during the executation of `#[init]` will automatically be caught,
302/// and an error code will be returned to the Wolfram Kernel.
303///
304// TODO: Mention which error code specifically:
305// `macro_utils::error_code::FAILED_WITH_PANIC`.
306///
307/// If the initialization function panics, the Wolfram Kernel will prevent other LibraryLink
308/// functions exported from that library from being loaded.
309///
310/// # Example
311///
312/// Define an initialization function:
313///
314/// ```rust
315/// use wolfram_library_link as wll;
316///
317/// #[wll::init]
318/// fn init_my_library() {
319/// println!("library is now initialized");
320/// }
321/// ```
322///
323/// # Behavior
324///
325/// If a library exports a function [called `WolframLibrary_initialize()`][lib-init], that
326/// function will automatically be called by the Wolfram Kernel when the library is
327/// loaded.
328///
329/// `#[init]` works by generating a definition for `WolframLibrary_initialize()`.
330///
331/// [lib-init]: https://reference.wolfram.com/language/LibraryLink/tutorial/LibraryStructure.html#280210622
332pub use wolfram_library_link_macros::init;
333
334
335/// Export the specified functions as native *LibraryLink* functions.
336///
337/// To be exported by this macro, the specified function(s) must implement
338/// [`NativeFunction`].
339///
340/// Functions exported using this macro will automatically:
341///
342/// * Call [`initialize()`] to initialize this library.
343/// * Catch any panics that occur.
344/// - If a panic does occur, the function will return
345/// [`LIBRARY_FUNCTION_ERROR`][crate::sys::LIBRARY_FUNCTION_ERROR].
346///
347// * Extract the function arguments from the raw [`MArgument`] array.
348// * Store the function return value in the raw [`MArgument`] return value field.
349///
350/// # Syntax
351///
352/// Export a native function.
353///
354/// ```
355/// # mod scope {
356/// # use wolfram_library_link::export;
357/// #[export]
358/// # fn square(x: i64) -> i64 { x }
359/// # }
360/// ```
361///
362/// Export a function using the specified low-level shared library symbol name.
363///
364/// ```
365/// # mod scope {
366/// # use wolfram_library_link::export;
367/// #[export(name = "WL_square")]
368/// # fn square(x: i64) -> i64 { x }
369/// # }
370/// ```
371///
372/// ### Advanced
373///
374/// Don't include an exported function in the automatic
375/// [`generate_loader!`] output.
376///
377/// ```
378/// # mod scope {
379/// # use wolfram_library_link::export;
380/// #[export(hidden)]
381/// # fn square(x: i64) -> i64 { x }
382/// # }
383/// ```
384///
385/// # Examples
386///
387/// ### Primitive data types
388///
389/// Export a native function with a single integer argument:
390///
391/// ```
392/// # mod scope {
393/// # use wolfram_library_link::export;
394/// #[export]
395/// fn square(x: i64) -> i64 {
396/// x * x
397/// }
398/// # }
399/// ```
400///
401/// ```wolfram
402/// LibraryFunctionLoad["...", "square", {Integer}, Integer]
403/// ```
404///
405/// Export a native function with a single string argument:
406///
407/// ```
408/// # mod scope {
409/// # use wolfram_library_link::export;
410/// #[export]
411/// fn reverse_string(string: String) -> String {
412/// string.chars().rev().collect()
413/// }
414/// # }
415/// ```
416///
417/// ```wolfram
418/// LibraryFunctionLoad["...", "reverse_string", {String}, String]
419/// ```
420///
421/// Export a native function with multiple arguments:
422///
423/// ```
424/// # mod scope {
425/// # use wolfram_library_link::export;
426/// #[export]
427/// fn times(a: f64, b: f64) -> f64 {
428/// a * b
429/// }
430/// # }
431/// ```
432///
433/// ```wolfram
434/// LibraryFunctionLoad["...", "times", {Real, Real}, Real]
435/// ```
436///
437/// ### Numeric arrays
438///
439/// Export a native function with a [`NumericArray`] argument:
440///
441/// ```
442/// # mod scope {
443/// # use wolfram_library_link::{export, NumericArray};
444/// #[export]
445/// fn total_i64(list: &NumericArray<i64>) -> i64 {
446/// list.as_slice().into_iter().sum()
447/// }
448/// # }
449/// ```
450///
451/// ```wolfram
452/// LibraryFunctionLoad[
453/// "...", "total_i64",
454/// {LibraryDataType[NumericArray, "Integer64"]}
455/// Integer
456/// ]
457/// ```
458///
459/// ### Customize exported function name
460///
461/// By default, the exported name of a function exported to the Wolfram Langauge
462/// using `#[export]` will be the same as the Rust function name. The exported name
463/// can be customed by specifying the `name = "..."` argument to the `#[export]` macro:
464///
465/// ```
466/// # mod scope {
467/// use wolfram_library_link::export;
468///
469/// #[export(name = "native_square")]
470/// fn square(x: i64) -> i64 {
471/// x * x
472/// }
473/// # }
474/// ```
475///
476/// ```wolfram
477/// LibraryFunctionLoad["<library name>", "native_square", {Integer}, Integer]
478/// ```
479///
480///
481// TODO: Add a "Memory Management" section to this comment and discuss "Constant".
482//
483// ```wolfram
484// LibraryFunctionLoad[
485// "...", "total_i64",
486// {
487// {LibraryDataType[NumericArray, "Integer64"], "Constant"}
488// },
489// Integer
490// ]
491// ```
492///
493/// # Parameter types
494///
495/// When manually writing the Wolfram
496/// [`LibraryFunctionLoad`][ref/LibraryFunctionLoad]<sub>WL</sub> call necessary to load
497/// a Rust *LibraryLink* function, you must declare the type signature of the function
498/// using the appropriate types.
499///
500/// The following table describes the relationship between Rust types that can be used as
501/// parameter types in a native LibraryLink function (namely: those that implement
502/// [`FromArg`]) and the compatible Wolfram *LibraryLink* function parameter type(s).
503///
504/// [`FromArg::parameter_type()`] can be used to determine the Wolfram library function
505/// parameter type programatically.
506///
507/// If you would prefer to have the Wolfram Language code for loading your library be
508/// generated automatically, use the [`generate_loader!`] macro.
509///
510/// <h4 style="border-bottom: none; margin-bottom: 4px"> ⚠️ Warning! ⚠️ </h4>
511///
512/// Calling a *LibraryLink* function from the Wolfram Language that was loaded using the
513/// wrong parameter type may lead to undefined behavior! Ensure that the function
514/// parameter type declared in your Wolfram Language code matches the Rust function
515/// parameter type.
516///
517/// Rust parameter type | Wolfram library function parameter type
518/// -----------------------------------|---------------------------------------
519/// [`bool`] | `"Boolean"`
520/// [`mint`] | `Integer`
521/// [`mreal`][crate::sys::mreal] | `Real`
522/// [`mcomplex`][crate::sys::mcomplex] | `Complex`
523/// [`String`] | `String`
524/// [`CString`][std::ffi::CString] | `String`
525/// [`&NumericArray`][NumericArray] | a. `LibraryDataType[NumericArray]` <br/> b. `{LibraryDataType[NumericArray], "Constant"}`[^1]
526/// [`NumericArray`] | a. `{LibraryDataType[NumericArray], "Manual"}`[^1] <br/> b. `{LibraryDataType[NumericArray], "Shared"}`[^1]
527/// [`&NumericArray<T>`][NumericArray] | a. `LibraryDataType[NumericArray, `[`"..."`][ref/NumericArray]`]`[^1] <br/> b. `{LibraryDataType[NumericArray, "..."], "Constant"}`[^1]
528/// [`NumericArray<T>`] | a. `{LibraryDataType[NumericArray, "..."], "Manual"}`[^1] <br/> b. `{LibraryDataType[NumericArray, "..."], "Shared"}`[^1]
529/// [`DataStore`] | `"DataStore"`
530///
531/// # Return types
532///
533/// The following table describes the relationship between Rust types that implement
534/// [`IntoArg`] and the compatible Wolfram *LibraryLink* function return type.
535///
536/// [`IntoArg::return_type()`] can be used to determine the Wolfram library function
537/// parameter type programatically.
538///
539/// Rust return type | Wolfram library function return type
540/// -----------------------------------|---------------------------------------
541/// [`()`][`unit`] | `"Void"`
542/// [`bool`] | `"Boolean"`
543/// [`mint`] | `Integer`
544/// [`mreal`][crate::sys::mreal] | `Real`
545/// [`i8`], [`i16`], [`i32`] | `Integer`
546/// [`u8`], [`u16`], [`u32`] | `Integer`
547/// [`f32`] | `Real`
548/// [`mcomplex`][crate::sys::mcomplex] | `Complex`
549/// [`String`] | `String`
550/// [`NumericArray`] | `LibraryDataType[NumericArray]`
551/// [`NumericArray<T>`] | `LibraryDataType[NumericArray, `[`"..."`][ref/NumericArray][^1]`]`
552/// [`DataStore`] | `"DataStore"`
553///
554/// [^1]: The Details and Options section of the Wolfram Language
555/// [`NumericArray` reference page][ref/NumericArray] lists the available element
556/// types.
557///
558/// [ref/NumericArray]: https://reference.wolfram.com/language/ref/NumericArray.html
559/// [ref/LibraryFunctionLoad]: https://reference.wolfram.com/language/ref/LibraryFunctionLoad.html
560///
561///
562///
563/// <br/><br/><br/>
564///
565/// # `#[export(wstp)]`
566///
567/// Export the specified functions as native *LibraryLink* WSTP functions.
568///
569/// To be exported by this macro, the specified function(s) must implement
570/// [`WstpFunction`].
571///
572/// Functions exported using this macro will automatically:
573///
574/// * Call [`initialize()`][crate::initialize] to initialize this library.
575/// * Catch any panics that occur.
576/// - If a panic does occur, it will be returned as a [`Failure[...]`][ref/Failure]
577/// expression.
578///
579/// [ref/Failure]: https://reference.wolfram.com/language/ref/Failure.html
580///
581/// # Syntax
582///
583/// Export a LibraryLink WSTP function.
584///
585/// ```
586/// # mod scope {
587/// # use wolfram_library_link::{export, wstp::Link, expr::Expr};
588/// #[export(wstp)]
589/// # fn square(args: Vec<Expr>) -> Expr { todo!() }
590/// # }
591/// ```
592///
593/// Export a LibraryLink WSTP function using the specified low-level shared library symbol
594/// name.
595///
596/// ```
597/// # mod scope {
598/// # use wolfram_library_link::{export, wstp::Link, expr::Expr};
599/// #[export(wstp, name = "WL_square")]
600/// # fn square(args: Vec<Expr>) -> Expr { todo!() }
601/// # }
602/// ```
603///
604/// # Examples
605///
606/// ##### WSTP function that squares a single integer argument:
607///
608/// ```
609/// # mod scope {
610/// use wolfram_library_link::{export, wstp::Link};
611///
612/// #[export(wstp)]
613/// fn square_wstp(link: &mut Link) {
614/// // Get the number of elements in the arguments list.
615/// let arg_count = link.test_head("List").unwrap();
616///
617/// if arg_count != 1 {
618/// panic!("square_wstp: expected to get a single argument");
619/// }
620///
621/// // Get the argument value.
622/// let x = link.get_i64().expect("expected Integer argument");
623///
624/// // Write the return value.
625/// link.put_i64(x * x).unwrap();
626/// }
627/// # }
628/// ```
629///
630/// ```wolfram
631/// LibraryFunctionLoad["...", "square_wstp", LinkObject, LinkObject]
632/// ```
633///
634/// ##### WSTP function that computes the sum of a variable number of arguments:
635///
636/// ```
637/// # mod scope {
638/// use wolfram_library_link::{export, wstp::Link};
639///
640/// #[export(wstp)]
641/// fn total_args_i64(link: &mut Link) {
642/// // Check that we recieved a functions arguments list, and get the number of arguments.
643/// let arg_count: usize = link.test_head("List").unwrap();
644///
645/// let mut total: i64 = 0;
646///
647/// // Get each argument, assuming that they are all integers, and add it to the total.
648/// for _ in 0..arg_count {
649/// let term = link.get_i64().expect("expected Integer argument");
650/// total += term;
651/// }
652///
653/// // Write the return value to the link.
654/// link.put_i64(total).unwrap();
655/// }
656/// # }
657/// ```
658///
659/// ```wolfram
660/// LibraryFunctionLoad["...", "total_args_i64", LinkObject, LinkObject]
661/// ```
662pub use wolfram_library_link_macros::export;
663
664const BACKTRACE_ENV_VAR: &str = "LIBRARY_LINK_RUST_BACKTRACE";
665
666//======================================
667// Callbacks to the Wolfram Kernel
668//======================================
669
670/// Evaluate `expr` by calling back into the Wolfram Kernel.
671///
672/// TODO: Specify and document what happens if the evaluation of `expr` triggers a
673/// kernel abort (such as a `Throw[]` in the code).
674pub fn evaluate(expr: &Expr) -> Expr {
675 match try_evaluate(expr) {
676 Ok(returned) => returned,
677 Err(msg) => panic!(
678 "evaluate(): evaluation of expression failed: {}: \n\texpression: {}",
679 msg, expr
680 ),
681 }
682}
683
684/// Attempt to evaluate `expr`, returning an error if a WSTP transport error occurred
685/// or evaluation failed.
686pub fn try_evaluate(expr: &Expr) -> Result<Expr, String> {
687 with_link(|link: &mut Link| {
688 // Send an EvaluatePacket['expr].
689 let _: () = link
690 // .put_expr(&Expr! { EvaluatePacket['expr] })
691 .put_expr(&Expr::normal(Symbol::new("System`EvaluatePacket"), vec![
692 expr.clone(),
693 ]))
694 .map_err(|e| e.to_string())?;
695
696 let _: () = process_wstp_link(link)?;
697
698 let return_packet: Expr = link.get_expr().map_err(|e| e.to_string())?;
699
700 let returned_expr = match return_packet.kind() {
701 ExprKind::Normal(normal) => {
702 debug_assert!(normal.has_head(&Symbol::new("System`ReturnPacket")));
703 debug_assert!(normal.elements().len() == 1);
704 normal.elements()[0].clone()
705 },
706 _ => {
707 return Err(format!(
708 "try_evaluate(): returned expression was not ReturnPacket: {}",
709 return_packet
710 ))
711 },
712 };
713
714 Ok(returned_expr)
715 })
716}
717
718/// Returns `true` if the user has requested that the current evaluation be aborted.
719///
720/// Programs should finish what they are doing and return control of this thread to
721/// to the kernel as quickly as possible. They should not exit the process or
722/// otherwise terminate execution, simply return up the call stack.
723///
724/// Within Rust functions exported using [`#[export]`][crate::export] or
725/// [`#[export(wstp)]`][crate::export#exportwstp] (which generate a wrapper function that
726/// catches panics), `panic!()` can be used to quickly unwind the call stack to the
727/// appropriate place.
728/// Note that this will not work if the current library is built with
729/// `panic = "abort"`. See the [`panic`][panic-option] profile configuration option
730/// for more information.
731///
732/// [panic-option]: https://doc.rust-lang.org/cargo/reference/profiles.html#panic
733pub fn aborted() -> bool {
734 // TODO: Is this function thread safe? Can it be called from a thread other than the
735 // one the LibraryLink wrapper was originally invoked from?
736 let val: mint = unsafe { rtl::AbortQ() };
737 // TODO: What values can `val` be?
738 val == 1
739}
740
741// TODO: Instead of making these public, add new evaluate(..) alternative that
742// takes a WstpExpr type.
743fn process_wstp_link(link: &mut Link) -> Result<(), String> {
744 assert_main_thread();
745
746 let raw_link = unsafe { link.raw_link() };
747
748 // Process the packet on the link.
749 let code: i32 = unsafe { rtl::processWSLINK(raw_link as *mut _) };
750
751 if code == 0 {
752 let error_message = link
753 .error_message()
754 .unwrap_or_else(|| "unknown error occurred on WSTP Link".into());
755
756 return Err(error_message);
757 }
758
759 Ok(())
760}
761
762/// Enforce exclusive access to the link returned by `getWSLINK()`.
763fn with_link<F: FnOnce(&mut Link) -> R, R>(f: F) -> R {
764 assert_main_thread();
765
766 static LOCK: Lazy<Mutex<()>> = Lazy::new(|| Default::default());
767
768 let _guard = LOCK.lock().expect("failed to acquire LINK lock");
769
770 let lib = get_library_data().raw_library_data;
771
772 let unsafe_link: sys::WSLINK = unsafe { rtl::getWSLINK(lib) };
773 let mut unsafe_link: wstp::sys::WSLINK = unsafe_link as wstp::sys::WSLINK;
774
775 // Safety:
776 // By using LOCK to ensure exclusive access to the `getWSLINK()` value within
777 // safe code, we can be confident that this `&mut Link` will not alias with
778 // other references to the underling link object.
779 let link = unsafe { Link::unchecked_ref_cast_mut(&mut unsafe_link) };
780
781 f(link)
782}
783
784#[inline]
785fn bool_from_mbool(boole: sys::mbool) -> bool {
786 boole != 0
787}
788
789
790// TODO: Allow any type which implements FromExpr in wrapper parameter lists?
791
792/// Generate and export a "loader" function, which returns an Association containing the
793/// names and loaded forms of all functions exported by this library.
794///
795/// All functions exported by the [`#[export(..)]`][crate::export] macro will
796/// automatically be included in the Association returned by this function.
797///
798/// See also: [`exported_library_functions_association()`]
799///
800/// # Syntax
801///
802/// Generate and export an automatic loader function.
803///
804/// ```
805/// # use wolfram_library_link::generate_loader;
806/// generate_loader![load_my_library];
807/// ```
808///
809/// # Example
810///
811/// The following Rust program exports three primary functions via LibraryLink:
812///
813/// * `add2`
814/// * `flat_total_i64`
815/// * `time_since_epoch`
816///
817/// These functions are exported from the library using the
818/// [`#[export(..)]`][crate::export] macro. This makes them loadable using
819/// [`LibraryFunctionLoad`][ref/LibraryFunctionLoad]<sub>WL</sub>.
820///
821///
822/// ```
823/// # mod scope {
824/// use wolfram_library_link::{self as wll, NumericArray, expr::{Expr, Symbol}};
825///
826/// wll::generate_loader![load_my_library_functions];
827///
828/// #[wll::export]
829/// fn add2(x: i64, y: i64) -> i64 {
830/// x + y
831/// }
832///
833/// #[wll::export]
834/// fn flat_total_i64(list: &NumericArray<i64>) -> i64 {
835/// list.as_slice().into_iter().sum()
836/// }
837///
838/// #[wll::export(wstp)]
839/// fn time_since_epoch(args: Vec<Expr>) -> Expr {
840/// use std::time::{SystemTime, UNIX_EPOCH};
841///
842/// assert!(args.len() == 0, "expected no arguments, got {}", args.len());
843///
844/// let duration = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
845///
846/// Expr::normal(Symbol::new("System`Quantity"), vec![
847/// Expr::real(duration.as_secs_f64()),
848/// Expr::string("Seconds")
849/// ])
850/// }
851/// # }
852/// ```
853///
854/// However, writing out the correct invocations of
855/// [`LibraryFunctionLoad`][ref/LibraryFunctionLoad] can be tedious and error prone.
856/// `generate_loader!` provides an easier alternative way to load the functions
857/// exported by this library.
858///
859/// In addition to the three previously mentioned functions, this library also exports a
860/// fourth function, called `load_my_library_functions`.
861///
862/// Instead of writing three separate `LibraryFunctionLoad` calls, one for each exported
863/// function, you can instead load the single `load_my_library_functions` function, which,
864/// when called, will automatically load the other three functions exported by this
865/// library:
866///
867/// ```wolfram
868/// library = "example_library";
869///
870/// loadFunctions = LibraryFunctionLoad[library, "load_my_library_functions", LinkObject, LinkObject];
871///
872/// functions = loadFunctions[library];
873/// ```
874///
875/// The `functions` variable will be an association, with roughly the following content:
876///
877/// ```wolfram
878/// <|
879/// "add2" -> LibraryFunction["example_library", "add2", {Integer, Integer}, Integer],
880/// "flat_total_i64" -> LibraryFunction[
881/// "example_library",
882/// "flat_total_i64",
883/// {{LibraryDataType[NumericArray, "Integer64"], "Constant"}},
884/// Integer
885/// ],
886/// "time_since_epoch" -> LibraryFunction["example_library", "time_since_epoch", LinkObject]
887/// |>
888/// ```
889///
890/// As shown above, the `load_my_library_functions` function generated by
891/// `generate_loader!` has automatically mapped the Rust paramater and return types onto
892/// the appropriate Wolfram LibraryLink types.
893///
894/// Functions from the library can be called by applying arguments to the appropriate
895/// value from the `functions` association:
896///
897/// ```wolfram
898/// (* Returns 12 *)
899/// functions["add2"][4, 8]
900///
901/// (* Returns 6 *)
902/// functions["flat_total_i64"][NumericArray[{1, 2, 3}, "Integer64"]]
903///
904/// (* Returns Quantity[seconds_, "Seconds"], containing the current number of seconds
905/// since the Unix epoch time. *)
906/// functions["time_since_epoch"][]
907/// ```
908///
909/// [ref/LibraryFunctionLoad]: https://reference.wolfram.com/language/ref/LibraryFunctionLoad.html
910#[cfg(feature = "automate-function-loading-boilerplate")]
911#[macro_export]
912macro_rules! generate_loader {
913 ($name:ident) => {
914 // TODO: Use this anonymous `const` trick in #[export(..)] too.
915 const _: () = {
916 #[no_mangle]
917 pub unsafe extern "C" fn $name(
918 lib: $crate::sys::WolframLibraryData,
919 raw_link: $crate::wstp::sys::WSLINK,
920 ) -> std::os::raw::c_int {
921 $crate::macro_utils::load_library_functions_impl(lib, raw_link)
922 }
923 };
924 };
925}