Skip to main content

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}