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_unsupported;
122
123 /// Build a Lean `String` object without allocating an intermediate Rust
124 /// owned `String`.
125 #[must_use]
126 pub fn string_from_str<'lean>(runtime: &'lean LeanRuntime, value: &str) -> Obj<'lean> {
127 crate::abi::string::from_str(runtime, value)
128 }
129}
130
131#[cfg(feature = "fuzzing")]
132pub mod fuzz_entry;
133
134pub use crate::abi::traits::{LeanAbi, LeanCReprAbi};
135pub use crate::callback::{
136 LeanCallbackFlow, LeanCallbackHandle, LeanCallbackPayload, LeanCallbackStatus, LeanProgressCallback,
137 LeanProgressTick, LeanStringEvent,
138};
139pub use crate::error::{
140 CapturedEvent, DIAGNOSTIC_CAPTURE_DEFAULT_CAPACITY, DiagnosticCapture, HostFailure, HostStage,
141 LEAN_ERROR_MESSAGE_LIMIT, LeanCancelled, LeanDiagnosticCode, LeanError, LeanException, LeanExceptionKind,
142 LeanResult,
143};
144pub use crate::handle::{LeanDeclaration, LeanExpr, LeanLevel, LeanName};
145pub use crate::module::{
146 DecodeCallResult, LeanArgs, LeanBuiltCapability, LeanCapability, LeanCapabilityPreflight, LeanCheckedExportError,
147 LeanExportAbiRepr, LeanExportArgAbi, LeanExportOwnership, LeanExportResultConvention, LeanExportReturnAbi,
148 LeanExportSignature, LeanExportSymbolKind, LeanExported, LeanIo, LeanLibrary, LeanLibraryBundle,
149 LeanLibraryDependency, LeanLoaderCheck, LeanLoaderDiagnosticCode, LeanLoaderReport, LeanLoaderSeverity, LeanModule,
150 LeanModuleInitializer, LeanRuntimePreflight,
151};
152pub use crate::runtime::obj::{Obj, ObjRef};
153pub use crate::runtime::{LeanRuntime, LeanThreadGuard};
154
155/// Version of the `lean-rs` crate, matching `Cargo.toml`.
156pub const VERSION: &str = env!("CARGO_PKG_VERSION");
157
158#[cfg(test)]
159mod tests {
160 use super::VERSION;
161
162 #[test]
163 fn version_constant_matches_package() {
164 assert_eq!(VERSION, env!("CARGO_PKG_VERSION"));
165 }
166}