Skip to main content

alef/
lib.rs

1//! alef — polyglot binding generator.
2//!
3//! Top-level module re-exports for the consolidated `alef` crate.
4//! Each module corresponds to one of the former workspace member crates
5//! (alef-core, alef-codegen, ...). See README and CHANGELOG (v0.18.0)
6//! for the consolidation rationale.
7//!
8//! ## Extension API
9//!
10//! Consumers who need domain-specific codegen (e.g. HTTP service bindings)
11//! implement [`Extension`] and call [`run_with_extensions`] instead of `main`:
12//!
13//! ```rust,no_run
14//! fn main() -> std::process::ExitCode {
15//!     alef::run_with_extensions(vec![])
16//! }
17//! ```
18
19#![allow(missing_docs)]
20#![allow(
21    clippy::collapsible_else_if,
22    clippy::if_same_then_else,
23    clippy::match_like_matches_macro,
24    clippy::only_used_in_recursion
25)]
26
27pub mod adapters;
28pub mod backends;
29pub mod bin_cli;
30pub mod cli;
31pub mod codegen;
32pub mod component;
33pub mod core;
34pub mod docs;
35pub mod e2e;
36pub mod extensions;
37pub mod extract;
38pub(crate) mod process;
39pub mod publish;
40pub mod readme;
41pub mod scaffold;
42pub mod snippets;
43pub mod template;
44#[cfg(test)]
45pub(crate) mod test_support;
46
47pub use core::extension::{Extension, ExtensionConfig};
48pub use core::template_env::TemplateEnv;
49pub use extensions::template::TemplateExtension;
50
51pub use core::backend::GeneratedFile;
52pub use core::config::{E2eConfig, Language, ResolvedCrateConfig};
53pub use core::ir::{ApiSurface, EnumDef, TypeDef};
54pub use e2e::fixture::{Fixture, FixtureGroup, group_fixtures, load_fixtures};
55
56/// Run the alef CLI, threading the given extensions through the pipeline.
57///
58/// The built-in [`TemplateExtension`] is always prepended so consumers who
59/// pass `vec![]` still get `[[extensions.template]]` block support.
60///
61/// # Example
62///
63/// ```rust,no_run
64/// fn main() -> std::process::ExitCode {
65///     alef::run_with_extensions(vec![])
66/// }
67/// ```
68pub fn run_with_extensions(mut extensions: Vec<Box<dyn Extension>>) -> std::process::ExitCode {
69    use clap::Parser;
70
71    extensions.insert(0, Box::new(TemplateExtension));
72
73    let cli = bin_cli::args::Cli::parse();
74    bin_cli::helpers::init_tracing(cli.verbose, cli.quiet, cli.no_color);
75
76    if cli.jobs > 0 {
77        rayon::ThreadPoolBuilder::new()
78            .num_threads(cli.jobs)
79            .build_global()
80            .ok();
81    }
82
83    #[cfg(feature = "dylib-loader")]
84    match extensions::dylib::load_dylib_extensions_from_config(&cli.config) {
85        Ok(mut dylib_extensions) => extensions.append(&mut dylib_extensions),
86        Err(e) => {
87            tracing::error!("{e:#}");
88            return std::process::ExitCode::FAILURE;
89        }
90    }
91
92    let _ = EXTENSIONS.set(extensions);
93
94    match bin_cli::dispatch::run(cli) {
95        Ok(()) => std::process::ExitCode::SUCCESS,
96        Err(e) => {
97            tracing::error!("{e:#}");
98            std::process::ExitCode::FAILURE
99        }
100    }
101}
102
103/// Active extensions for the current pipeline run.
104///
105/// Populated by [`run_with_extensions`] before dispatch; accessed by
106/// pipeline stages via [`with_extensions`]. Process-global (not
107/// `thread_local!`) so rayon worker threads see the same list.
108pub(crate) static EXTENSIONS: std::sync::OnceLock<Vec<Box<dyn Extension>>> = std::sync::OnceLock::new();
109
110/// Run `f` with an immutable reference to the active extensions list.
111pub(crate) fn with_extensions<F, R>(f: F) -> R
112where
113    F: FnOnce(&[Box<dyn Extension>]) -> R,
114{
115    static EMPTY: Vec<Box<dyn Extension>> = Vec::new();
116    f(EXTENSIONS.get().unwrap_or(&EMPTY))
117}