baml/
lib.rs

1//! BAML Runtime for Rust
2//!
3//! This crate provides the runtime support for BAML-generated Rust code.
4//! Users should not import from this crate directly - instead, use the
5//! generated `baml_client` crate which re-exports necessary types.
6//!
7//! # Derive Macros
8//!
9//! Use `#[derive(BamlEncode)]` and `#[derive(BamlDecode)]` to automatically
10//! implement serialization for your types:
11//!
12//! ```ignore
13//! use baml::{BamlEncode, BamlDecode};
14//!
15//! #[derive(BamlEncode, BamlDecode)]
16//! #[baml(name = "Person")]
17//! struct Person {
18//!     name: String,
19//!     #[baml(name = "years_old")]
20//!     age: i64,
21//! }
22//! ```
23
24mod args;
25mod async_stream;
26mod client_registry;
27mod codec;
28mod error;
29mod ffi;
30mod known_types;
31pub mod platform;
32mod raw_objects;
33mod runtime;
34mod stream;
35mod types;
36
37// Proto module generated by prost-build
38mod proto {
39    #![allow(clippy::empty_structs_with_brackets)]
40    pub(crate) mod baml_cffi_v1 {
41        include!(concat!(env!("OUT_DIR"), "/baml.cffi.v1.rs"));
42    }
43}
44
45use std::ffi::CString;
46
47// Public API - re-exported through baml_client
48pub use args::{CancellationToken, FunctionArgs};
49// New dynamic type exports
50pub use async_stream::AsyncStreamingCall;
51// Re-export derive macros
52pub use baml_macros::{BamlDecode, BamlEncode};
53pub use client_registry::ClientRegistry;
54pub use codec::{
55    decode_enum, decode_field, encode_class, encode_class_dynamic, encode_enum, BamlClass,
56    BamlDecode, BamlEncode, BamlEnum, BamlSerializeMapKey, BamlValue, DynamicClass, DynamicEnum,
57    DynamicUnion, FromBamlValue, FromBamlValueRef, KnownTypes,
58};
59pub use error::BamlError;
60pub use raw_objects::{
61    // Media types
62    Audio,
63    // TypeBuilder types
64    ClassBuilder,
65    ClassPropertyBuilder,
66    // Collector types
67    Collector,
68    EnumBuilder,
69    EnumValueBuilder,
70    FunctionLog,
71    // HTTP types
72    HTTPBody,
73    HTTPRequest,
74    HTTPResponse,
75    Image,
76    // LLM call types
77    LLMCall,
78    LLMCallKind,
79    LLMStreamCall,
80    LogType,
81    Pdf,
82    // SSE types
83    SSEResponse,
84    // Timing types
85    StreamTiming,
86    Timing,
87    TypeBuilder,
88    TypeDef,
89    Usage,
90    Video,
91};
92pub use runtime::{BamlRuntime, StaticRuntimeType};
93pub use stream::{StreamEvent, StreamingCall};
94pub use types::{Check, CheckStatus, Checked, StreamState, StreamingState};
95
96/// Internal module for derive macro support.
97///
98/// This module exposes proto types needed by the generated derive macro code.
99/// Do not use directly - use the derive macros instead.
100#[doc(hidden)]
101pub mod __internal {
102    use crate::{codec::traits::BamlClass, error::BamlError};
103    pub use crate::{ffi::callbacks, proto::baml_cffi_v1::*};
104
105    /// Decode a class from a `CffiValueHolder`.
106    /// Used by derive macros to implement `BamlDecode` for structs.
107    pub fn decode_class<T: BamlClass>(holder: &CffiValueHolder) -> Result<T, BamlError> {
108        match &holder.value {
109            Some(cffi_value_holder::Value::ClassValue(class)) => T::from_class_value(class),
110            other => Err(BamlError::internal(format!(
111                "expected class {}, got {:?}",
112                T::TYPE_NAME,
113                other.as_ref().map(variant_name)
114            ))),
115        }
116    }
117
118    /// Extract the inner value and variant name from a `UnionVariantValue`.
119    /// Returns (`variant_name`, `inner_value`) or an error if the holder is not
120    /// a `UnionVariantValue`.
121    pub fn extract_union_variant_with_name<'a>(
122        type_name: &str,
123        holder: &'a CffiValueHolder,
124    ) -> Result<(&'a str, &'a CffiValueHolder), BamlError> {
125        match &holder.value {
126            Some(cffi_value_holder::Value::UnionVariantValue(union)) => {
127                let inner = union
128                    .value
129                    .as_ref()
130                    .map(std::convert::AsRef::as_ref)
131                    .ok_or_else(|| BamlError::internal("union variant missing inner value"))?;
132                Ok((union.value_option_name.as_str(), inner))
133            }
134            _ => Err(BamlError::internal(format!(
135                "expected union variant of type {type_name}, got {holder:?}"
136            ))),
137        }
138    }
139
140    fn variant_name(v: &cffi_value_holder::Value) -> &'static str {
141        match v {
142            cffi_value_holder::Value::NullValue(_) => "null",
143            cffi_value_holder::Value::StringValue(_) => "string",
144            cffi_value_holder::Value::IntValue(_) => "int",
145            cffi_value_holder::Value::FloatValue(_) => "float",
146            cffi_value_holder::Value::BoolValue(_) => "bool",
147            cffi_value_holder::Value::ClassValue(_) => "class",
148            cffi_value_holder::Value::EnumValue(_) => "enum",
149            cffi_value_holder::Value::LiteralValue(_) => "literal",
150            cffi_value_holder::Value::ObjectValue(_) => "object",
151            cffi_value_holder::Value::ListValue(_) => "list",
152            cffi_value_holder::Value::MapValue(_) => "map",
153            cffi_value_holder::Value::UnionVariantValue(_) => "union",
154            cffi_value_holder::Value::CheckedValue(_) => "checked",
155            cffi_value_holder::Value::StreamingStateValue(_) => "streaming_state",
156        }
157    }
158}
159
160/// Call baml-cli with the given arguments
161/// Returns the exit code
162pub fn invoke_cli(args: &[&str]) -> i32 {
163    // Convert args to C strings
164    let c_args: Vec<CString> = args
165        .iter()
166        .map(|s| CString::new(*s).expect("invalid arg"))
167        .collect();
168
169    // Create array of pointers
170    let c_arg_ptrs: Vec<*const libc::c_char> = c_args
171        .iter()
172        .map(|s| s.as_ptr())
173        .chain(std::iter::once(std::ptr::null())) // null terminator
174        .collect();
175
176    #[allow(unsafe_code, clippy::print_stderr)]
177    unsafe {
178        match ffi::invoke_runtime_cli(c_arg_ptrs.as_ptr()) {
179            Ok(code) => code,
180            Err(e) => {
181                // CLI errors should be printed to stderr
182                eprintln!("Failed to load BAML library: {e}");
183                1
184            }
185        }
186    }
187}
188
189/// Get the BAML library version
190pub fn version() -> String {
191    match ffi::version() {
192        Ok(v) => v,
193        Err(_) => "unknown".to_string(),
194    }
195}