Skip to main content

lean_rs/
lib.rs

1//! Safe Rust FFI primitive for embedding Lean 4 from a host application.
2//!
3//! `lean-rs` is the typed FFI binding to the Lean 4 runtime—the
4//! minimum surface a Rust crate needs to drive a compiled Lean library:
5//! bring the runtime up, open a Lake-built capability bundle, initialise a
6//! module, and call typed `@[export]` functions with first-class type
7//! marshalling. The standard Lean service layer (`LeanHost`,
8//! `LeanCapabilities`, `LeanSession`, plus the evidence and meta surfaces)
9//! lives in the sibling
10//! [`lean-rs-host`](https://docs.rs/lean-rs-host) crate, with its own
11//! 28+9 `lean_rs_host_*` Lean shim contract. This crate ships only the generic
12//! interop shims used by same-process callbacks; it has no theorem-prover host shim
13//! contract.
14//!
15//! ## Happy Path
16//!
17//! Bring the runtime up once, open the build-script produced capability,
18//! initialise the module, look up a typed export, and call it:
19//!
20//! ```ignore
21//! let runtime = lean_rs::LeanRuntime::init()?;
22//! let capability = lean_rs::LeanCapability::from_build_manifest(
23//!     runtime,
24//!     lean_rs::LeanBuiltCapability::manifest_path(env!("MY_CAPABILITY_MANIFEST")),
25//! )?;
26//! let add = capability.exported::<(u64, u64), u64>("my_export_add")?;
27//! let sum     = add.call(3, 4)?;
28//! ```
29//!
30//! [`LeanRuntime::init`] is the single doorway. It brings Lean up
31//! (process-once, idempotent) and returns a `'static` borrow that
32//! anchors the `'lean` lifetime every later handle carries; use-before-
33//! init is structurally impossible.
34//!
35//! Worker threads that did not start inside Lean must be attached for
36//! the duration of their Lean work via [`LeanThreadGuard::attach`];
37//! see `docs/architecture/04-concurrency.md` for the contract.
38//!
39//! ## Module map
40//!
41//! - [`error`]—typed error boundary. [`LeanError`] is a three-variant
42//!   enum (`LeanException` for Lean-thrown `IO` errors, `Host` for
43//!   host failures, `Cancelled` for cooperative host
44//!   cancellation); payload structs ([`LeanException`],
45//!   [`HostFailure`], [`LeanCancelled`]) have private fields so the
46//!   bounded-message invariant is structural. Every error projects to a
47//!   [`LeanDiagnosticCode`] via `.code()`. The in-process
48//!   [`DiagnosticCapture`] RAII guard lets tests assert on `tracing`
49//!   events without installing a global subscriber.
50//! - [`module`]—load a Lake-built Lean capability and call typed
51//!   exported functions. [`LeanCapability`] is the normal shipped-crate
52//!   surface; [`LeanCapabilityPreflight`] reports package/loader problems
53//!   before `dlopen`; [`LeanLibraryBundle`] anchors dependency dylibs;
54//!   [`LeanLibrary`] is the advanced one-dylib RAII handle; [`LeanModule`]
55//!   proves a module's initializer succeeded; [`LeanCapability::exported`]
56//!   is the safe checked lookup path; [`LeanModule::exported_unchecked`] is
57//!   the explicit unsafe path for arbitrary exports; [`LeanExported`] is a
58//!   single generic typed function handle whose `.call` impl is macro-stamped
59//!   per arity `0..=12`.
60//! - [`handle`]—opaque, lifetime-bound receipts for the four core
61//!   Lean semantic values ([`LeanName`], [`LeanLevel`], [`LeanExpr`],
62//!   [`LeanDeclaration`]). Construction and inspection happen Lean-side
63//!   through [`LeanModule::exported_unchecked`] against caller-authored shims.
64//! - [`callback`]—RAII callback registrations for Lean-to-Rust calls.
65//!   [`LeanCallbackHandle`] hides the registry id, payload decoder, and
66//!   trampoline while still producing the two `USize` ABI values generic
67//!   interop shims pass to Lean.
68//! - [`runtime`]—process-wide [`LeanRuntime`] anchor,
69//!   [`LeanThreadGuard`] attach RAII, and the lifetime-bound owned /
70//!   borrowed object handles [`Obj`] / [`ObjRef`].
71//! - [`abi`]—sealed [`LeanAbi`] trait + per-Lean-type C-ABI
72//!   representation impls. The trait is the bound on
73//!   [`LeanExported`]'s argument and return types; the impls are
74//!   crate-internal (sealing prevents downstream `LeanAbi for MyType`).
75//!
76//! ## Layering
77//!
78//! `lean-rs-sys → lean-toolchain → lean-rs → lean-rs-host`. The first
79//! two crates expose raw FFI and toolchain metadata; this crate is the
80//! safe surface every direct typed-FFI consumer depends on. The
81//! standard Lean service layer lives in `lean-rs-host`, and worker
82//! process facades live in `lean-rs-worker-*`.
83//! Embedders that genuinely need the raw `lean_*` symbols can depend
84//! on `lean-rs-sys` directly, accepting its full `unsafe` discipline.
85//!
86//! ## Curation policy
87//!
88//! Items at `lean_rs::*` are the curated semver surface. The crate
89//! root re-exports the typed-FFI primitive plus the four handle types
90//! and the `lean-rs` error model. Refactors that reshape internal modules
91//! are free as long as those re-exports stay stable. The public-
92//! surface baseline lives at `docs/api-review/lean-rs-public.txt`.
93
94pub mod abi;
95pub mod callback;
96pub mod error;
97pub mod handle;
98pub mod module;
99pub mod runtime;
100
101/// **Internal extension point.** Not part of the public API; not covered
102/// by semver. Exists so the sibling `lean-rs-host` crate can construct
103/// `LeanError` values via the narrow constructor wrappers it
104/// uses without bypassing the structural bounding invariant: external
105/// callers cannot mint `LeanError` values with unbounded messages. The
106/// internal bridge stays narrow on purpose:
107/// every extra re-export here is interface surface external readers
108/// might mistake for a stable API. Add a wrapper only when a real call
109/// site needs it.
110///
111/// External crates must not depend on anything under this path.
112#[doc(hidden)]
113pub mod __host_internals {
114    use crate::runtime::LeanRuntime;
115    use crate::runtime::obj::Obj;
116
117    pub use crate::error::host_callback_panic;
118    pub use crate::error::host_cancelled;
119    pub use crate::error::host_internal;
120    pub use crate::error::host_module_init;
121    pub use crate::error::host_resource_exhausted;
122    pub use crate::error::host_resource_exhausted_with_facts;
123    pub use crate::error::host_unsupported;
124
125    /// Build a Lean `String` object without allocating an intermediate Rust
126    /// owned `String`.
127    #[must_use]
128    pub fn string_from_str<'lean>(runtime: &'lean LeanRuntime, value: &str) -> Obj<'lean> {
129        crate::abi::string::from_str(runtime, value)
130    }
131
132    /// Set Lean's process-global runtime memory limit for an isolated worker
133    /// or profiling process.
134    ///
135    /// This is a guardrail only. It can make Lean throw before the OS kills
136    /// the process, but it does not free retained runtime/import state.
137    pub fn set_runtime_memory_limit_bytes_for_guardrail(limit_bytes: usize) {
138        crate::runtime::memory::set_memory_limit_bytes_for_guardrail(limit_bytes);
139    }
140}
141
142#[cfg(feature = "fuzzing")]
143pub mod fuzz_entry;
144
145pub use crate::abi::traits::{LeanAbi, LeanCReprAbi};
146pub use crate::callback::{
147    LeanCallbackFlow, LeanCallbackHandle, LeanCallbackPayload, LeanCallbackStatus, LeanProgressCallback,
148    LeanProgressTick, LeanStringEvent,
149};
150pub use crate::error::{
151    CapturedEvent, DIAGNOSTIC_CAPTURE_DEFAULT_CAPACITY, DiagnosticCapture, HostFailure, HostStage,
152    LEAN_ERROR_MESSAGE_LIMIT, LeanCancelled, LeanDiagnosticCode, LeanError, LeanException, LeanExceptionKind,
153    LeanResult, ResourceExhaustedFacts,
154};
155pub use crate::handle::{LeanDeclaration, LeanExpr, LeanLevel, LeanName};
156pub use crate::module::{
157    DecodeCallResult, LeanArgs, LeanBuiltCapability, LeanCapability, LeanCapabilityPreflight, LeanCheckedExportError,
158    LeanExportAbiRepr, LeanExportArgAbi, LeanExportOwnership, LeanExportResultConvention, LeanExportReturnAbi,
159    LeanExportSignature, LeanExportSymbolKind, LeanExported, LeanIo, LeanLibrary, LeanLibraryBundle,
160    LeanLibraryDependency, LeanLoaderCheck, LeanLoaderDiagnosticCode, LeanLoaderReport, LeanLoaderSeverity, LeanModule,
161    LeanModuleInitializer, LeanRuntimePreflight,
162};
163pub use crate::runtime::obj::{Obj, ObjRef};
164pub use crate::runtime::{LeanRuntime, LeanThreadGuard};
165
166/// Version of the `lean-rs` crate, matching `Cargo.toml`.
167pub const VERSION: &str = env!("CARGO_PKG_VERSION");
168
169#[cfg(test)]
170mod tests {
171    use super::VERSION;
172
173    #[test]
174    fn version_constant_matches_package() {
175        assert_eq!(VERSION, env!("CARGO_PKG_VERSION"));
176    }
177}