raw_wstp_function/
raw_wstp_function.rs

1//! This example demonstrates using the raw Rust wrappers around the LibraryLink C API to
2//! write a function which looks much like a classic C function using LibraryLink and
3//! WSTP would.
4//!
5//! This also includes an example of mixing the low-level LibraryLink bindings with the
6//! higher-level bindings provided by the `wstp` crate.
7
8use std::os::raw::{c_int, c_uint};
9
10use wolfram_library_link::{
11    expr::{Expr, Symbol},
12    sys::{
13        self as wll_sys, WolframLibraryData, LIBRARY_FUNCTION_ERROR, LIBRARY_NO_ERROR,
14    },
15    wstp::{
16        sys::{WSGetInteger, WSNewPacket, WSPutInteger, WSTestHead, WSLINK},
17        Link,
18    },
19};
20
21/// This function is loaded by evaluating:
22///
23/// ```wolfram
24/// LibraryFunctionLoad[
25///     "/path/to/libraw_wstp_function.dylib",
26///     "demo_wstp_function",
27///     LinkObject,
28///     LinkObject
29/// ]
30/// ```
31#[no_mangle]
32pub unsafe extern "C" fn demo_wstp_function(
33    _lib: WolframLibraryData,
34    link: WSLINK,
35) -> c_uint {
36    let mut i1: c_int = 0;
37    let mut i2: c_int = 0;
38    let mut len: c_int = 0;
39
40    if WSTestHead(link, b"List\0".as_ptr() as *const i8, &mut len) == 0 {
41        return LIBRARY_FUNCTION_ERROR;
42    }
43    if len != 2 {
44        return LIBRARY_FUNCTION_ERROR;
45    }
46
47    if WSGetInteger(link, &mut i1) == 0 {
48        return LIBRARY_FUNCTION_ERROR;
49    }
50    if WSGetInteger(link, &mut i2) == 0 {
51        return LIBRARY_FUNCTION_ERROR;
52    }
53    if WSNewPacket(link) == 0 {
54        return LIBRARY_FUNCTION_ERROR;
55    }
56
57    let sum = i1 + i2;
58
59    if WSPutInteger(link, sum) == 0 {
60        return LIBRARY_FUNCTION_ERROR;
61    }
62
63    return LIBRARY_NO_ERROR;
64}
65
66/// This example shows how the raw Rust wrappers can be mixed with higher-level wrappers
67/// around the Wolfram Symbolic Transfer Protocal (WSTP) for conveniently calling back
68/// into the Kernel to perform an evaluation.
69///
70/// This function is loaded by evaluating:
71///
72/// ```wolfram
73/// LibraryFunctionLoad[
74///     "/path/to/libraw_wstp_function.dylib",
75///     "demo_wstp_function_callback",
76///     LinkObject,
77///     LinkObject
78/// ]
79/// ```
80#[no_mangle]
81pub extern "C" fn demo_wstp_function_callback(
82    lib: WolframLibraryData,
83    mut link: WSLINK,
84) -> c_uint {
85    // Create a safe Link wrapper around the raw `WSLINK`. This is a borrowed rather than
86    // owned Link because the caller (the Kernel) owns the link.
87    let link: &mut Link = unsafe { Link::unchecked_ref_cast_mut(&mut link) };
88
89    // Skip reading the argument list packet.
90    if link.raw_get_next().and_then(|_| link.new_packet()).is_err() {
91        return LIBRARY_FUNCTION_ERROR;
92    }
93
94    let callback_link = unsafe { (*lib).getWSLINK.unwrap()(lib) };
95    let mut callback_link = callback_link as wstp::sys::WSLINK;
96
97    {
98        let safe_callback_link =
99            unsafe { Link::unchecked_ref_cast_mut(&mut callback_link) };
100
101        safe_callback_link
102            // EvaluatePacket[Print["Hello, World! --- WSTP"]]
103            .put_expr(&Expr::normal(Symbol::new("System`EvaluatePacket"), vec![
104                Expr::normal(Symbol::new("System`Print"), vec![Expr::string(
105                    "Hello, World! --- WSTP",
106                )]),
107            ]))
108            .unwrap();
109
110        unsafe {
111            (*lib).processWSLINK.unwrap()(
112                safe_callback_link.raw_link() as wll_sys::WSLINK
113            );
114        }
115
116        // Skip the return value packet. This is necessary, otherwise the link has
117        // unread data and the return value of this function cannot be processed properly.
118        if safe_callback_link
119            .raw_get_next()
120            .and_then(|_| safe_callback_link.new_packet())
121            .is_err()
122        {
123            return LIBRARY_FUNCTION_ERROR;
124        }
125    }
126
127    link.put_expr(&Expr::string("returned normally")).unwrap();
128
129    return LIBRARY_NO_ERROR;
130}
131
132/// This example makes use of the [`wstp`][wstp] crate to provide a safe wrapper around
133/// around the WSTP link object, which can be used to read the argument expression and
134/// write out the return expression.
135///
136/// ```wolfram
137/// function = LibraryFunctionLoad[
138///     "raw_wstp_function",
139///     "wstp_expr_function",
140///     LinkObject,
141///     LinkObject
142/// ];
143/// ```
144#[no_mangle]
145pub extern "C" fn wstp_expr_function(
146    _lib: WolframLibraryData,
147    mut unsafe_link: WSLINK,
148) -> c_uint {
149    let link: &mut Link = unsafe { Link::unchecked_ref_cast_mut(&mut unsafe_link) };
150
151    let expr = match link.get_expr() {
152        Ok(expr) => expr,
153        Err(err) => {
154            // Skip reading the argument list packet.
155            if link.raw_get_next().and_then(|_| link.new_packet()).is_err() {
156                return LIBRARY_FUNCTION_ERROR;
157            }
158
159            let err = Expr::string(err.to_string());
160            let err = Expr::normal(Symbol::new("System`Failure"), vec![
161                Expr::string("WSTP Error"),
162                Expr::normal(Symbol::new("System`Association"), vec![Expr::normal(
163                    Symbol::new("System`Rule"),
164                    vec![Expr::string("Message"), err],
165                )]),
166            ]);
167            match link.put_expr(&err) {
168                Ok(()) => return LIBRARY_NO_ERROR,
169                Err(_) => return LIBRARY_FUNCTION_ERROR,
170            }
171        },
172    };
173
174    let expr_string = format!("Input: {}", expr.to_string());
175
176    match link.put_expr(&Expr::string(expr_string)) {
177        Ok(()) => LIBRARY_NO_ERROR,
178        Err(_) => LIBRARY_FUNCTION_ERROR,
179    }
180}