baml_sys/
lib.rs

1//! BAML FFI bindings with runtime dynamic library loading.
2//!
3//! This crate provides low-level FFI bindings to the BAML runtime library.
4//! The library is loaded dynamically at runtime using `libloading`.
5//!
6//! # Library Resolution
7//!
8//! The library is searched in the following order:
9//! 1. Explicit path set via [`set_library_path()`]
10//! 2. `BAML_LIBRARY_PATH` environment variable
11//! 3. User cache directory (`~/.cache/baml/libs/{VERSION}/`)
12//! 4. Auto-download from GitHub releases (if `download` feature enabled)
13//! 5. System default paths (`/usr/local/lib/`, etc.)
14//!
15//! # Usage
16//!
17//! ## Runtime Loading (Default)
18//!
19//! ```rust,ignore
20//! use baml_sys::{get_symbols, version};
21//!
22//! // Library is loaded on first access
23//! let v = version()?;
24//! println!("BAML version: {v}");
25//! ```
26//!
27//! ## Build-time Hook
28//!
29//! In your `build.rs`:
30//!
31//! ```rust,ignore
32//! fn main() {
33//!     // Ensure library is available before build completes
34//!     let lib_path = baml_sys::ensure_library()
35//!         .expect("Failed to find/download BAML library");
36//!     println!("cargo:rerun-if-changed={}", lib_path.display());
37//! }
38//! ```
39//!
40//! # Environment Variables
41//!
42//! - `BAML_LIBRARY_PATH`: Explicit path to the library
43//! - `BAML_CACHE_DIR`: Override cache directory location
44//! - `BAML_LIBRARY_DISABLE_DOWNLOAD`: Set to "true" to disable auto-download
45
46#![warn(missing_docs)]
47
48mod download;
49mod error;
50mod loader;
51mod symbols;
52
53use std::ffi::CStr;
54
55pub use error::{BamlSysError, Result};
56use libc::{c_char, c_int, c_void, size_t};
57pub use loader::{
58    ensure_library, set_library_path, ENV_CACHE_DIR, ENV_DISABLE_DOWNLOAD, ENV_LIBRARY_PATH,
59    VERSION,
60};
61pub use symbols::{get_symbols, Buffer, CallbackFn, OnTickCallbackFn, Symbols};
62
63// ============================================================================
64// Safe wrapper functions
65// ============================================================================
66
67/// Get the BAML library version.
68pub fn version() -> Result<String> {
69    let symbols = get_symbols()?;
70    // Safety: version() returns a valid C string from the library
71    #[allow(unsafe_code)]
72    let ptr = unsafe { (symbols.version)() };
73    #[allow(unsafe_code)]
74    let cstr = unsafe { CStr::from_ptr(ptr) };
75    Ok(cstr.to_string_lossy().into_owned())
76}
77
78/// Register callbacks for FFI operations.
79///
80/// # Safety
81/// The callbacks must remain valid for the lifetime of the program.
82#[allow(unsafe_code)]
83pub unsafe fn register_callbacks(
84    callback_fn: CallbackFn,
85    error_callback_fn: CallbackFn,
86    on_tick_callback_fn: OnTickCallbackFn,
87) -> Result<()> {
88    let symbols = get_symbols()?;
89    unsafe {
90        (symbols.register_callbacks)(callback_fn, error_callback_fn, on_tick_callback_fn);
91    }
92    Ok(())
93}
94
95/// Create a new BAML runtime.
96///
97/// # Safety
98/// All pointers must be valid C strings.
99#[allow(unsafe_code)]
100pub unsafe fn create_baml_runtime(
101    root_path: *const c_char,
102    src_files_json: *const c_char,
103    env_vars_json: *const c_char,
104) -> Result<*const c_void> {
105    let symbols = get_symbols()?;
106    Ok(unsafe { (symbols.create_baml_runtime)(root_path, src_files_json, env_vars_json) })
107}
108
109/// Destroy a BAML runtime.
110///
111/// # Safety
112/// The runtime pointer must be valid and not already destroyed.
113#[allow(unsafe_code)]
114pub unsafe fn destroy_baml_runtime(runtime: *const c_void) -> Result<()> {
115    let symbols = get_symbols()?;
116    unsafe {
117        (symbols.destroy_baml_runtime)(runtime);
118    }
119    Ok(())
120}
121
122/// Invoke the runtime CLI.
123///
124/// # Safety
125/// The args pointer must be a valid null-terminated array of C strings.
126#[allow(unsafe_code)]
127pub unsafe fn invoke_runtime_cli(args: *const *const c_char) -> Result<c_int> {
128    let symbols = get_symbols()?;
129    Ok(unsafe { (symbols.invoke_runtime_cli)(args) })
130}
131
132/// Call a BAML function.
133///
134/// # Safety
135/// All pointers must be valid.
136#[allow(unsafe_code)]
137pub unsafe fn call_function_from_c(
138    runtime: *const c_void,
139    function_name: *const c_char,
140    encoded_args: *const c_char,
141    length: size_t,
142    id: u32,
143) -> Result<*const c_void> {
144    let symbols = get_symbols()?;
145    Ok(unsafe { (symbols.call_function_from_c)(runtime, function_name, encoded_args, length, id) })
146}
147
148/// Call a BAML function with streaming.
149///
150/// # Safety
151/// All pointers must be valid.
152#[allow(unsafe_code)]
153pub unsafe fn call_function_stream_from_c(
154    runtime: *const c_void,
155    function_name: *const c_char,
156    encoded_args: *const c_char,
157    length: size_t,
158    id: u32,
159) -> Result<*const c_void> {
160    let symbols = get_symbols()?;
161    Ok(unsafe {
162        (symbols.call_function_stream_from_c)(runtime, function_name, encoded_args, length, id)
163    })
164}
165
166/// Call a BAML function for parsing.
167///
168/// # Safety
169/// All pointers must be valid.
170#[allow(unsafe_code)]
171pub unsafe fn call_function_parse_from_c(
172    runtime: *const c_void,
173    function_name: *const c_char,
174    encoded_args: *const c_char,
175    length: size_t,
176    id: u32,
177) -> Result<*const c_void> {
178    let symbols = get_symbols()?;
179    Ok(unsafe {
180        (symbols.call_function_parse_from_c)(runtime, function_name, encoded_args, length, id)
181    })
182}
183
184/// Cancel a function call.
185///
186/// # Safety
187/// The id must be a valid call ID.
188#[allow(unsafe_code)]
189pub unsafe fn cancel_function_call(id: u32) -> Result<*const c_void> {
190    let symbols = get_symbols()?;
191    Ok(unsafe { (symbols.cancel_function_call)(id) })
192}
193
194/// Call an object constructor.
195///
196/// # Safety
197/// The `encoded_invocation` pointer must be valid.
198#[allow(unsafe_code)]
199pub unsafe fn call_object_constructor(
200    encoded_invocation: *const c_char,
201    length: size_t,
202) -> Result<Buffer> {
203    let symbols = get_symbols()?;
204    Ok(unsafe { (symbols.call_object_constructor)(encoded_invocation, length) })
205}
206
207/// Call an object method.
208///
209/// # Safety
210/// All pointers must be valid.
211#[allow(unsafe_code)]
212pub unsafe fn call_object_method(
213    runtime: *const c_void,
214    encoded_invocation: *const c_char,
215    length: size_t,
216) -> Result<Buffer> {
217    let symbols = get_symbols()?;
218    Ok(unsafe { (symbols.call_object_method)(runtime, encoded_invocation, length) })
219}
220
221/// Free a buffer returned from object operations.
222///
223/// # Safety
224/// The buffer must have been returned from this library.
225#[allow(unsafe_code)]
226pub unsafe fn free_buffer(buf: Buffer) -> Result<()> {
227    let symbols = get_symbols()?;
228    unsafe {
229        (symbols.free_buffer)(buf);
230    }
231    Ok(())
232}
233
234/// Get the path to the loaded library.
235pub fn library_path() -> Result<std::path::PathBuf> {
236    let lib = loader::get_library()?;
237    Ok(lib.path.clone())
238}