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(clippy::if_same_then_else, clippy::match_like_matches_macro)]
21
22pub mod adapters;
23pub mod backends;
24pub mod bin_cli;
25pub mod cli;
26pub mod codegen;
27pub mod core;
28pub mod docs;
29pub mod e2e;
30pub mod extensions;
31pub mod extract;
32pub mod publish;
33pub mod readme;
34pub mod scaffold;
35pub mod snippets;
36
37pub use core::extension::{Extension, ExtensionConfig};
38pub use core::template_env::TemplateEnv;
39pub use extensions::template::TemplateExtension;
40
41pub use core::backend::GeneratedFile;
42pub use core::config::{E2eConfig, Language, ResolvedCrateConfig};
43pub use core::ir::{ApiSurface, EnumDef, TypeDef};
44pub use e2e::fixture::{Fixture, FixtureGroup, group_fixtures, load_fixtures};
45
46/// Run the alef CLI, threading the given extensions through the pipeline.
47///
48/// The built-in [`TemplateExtension`] is always prepended so consumers who
49/// pass `vec![]` still get `[[extensions.template]]` block support.
50///
51/// # Example
52///
53/// ```rust,no_run
54/// fn main() -> std::process::ExitCode {
55///     alef::run_with_extensions(vec![])
56/// }
57/// ```
58pub fn run_with_extensions(mut extensions: Vec<Box<dyn Extension>>) -> std::process::ExitCode {
59    use clap::Parser;
60
61    extensions.insert(0, Box::new(TemplateExtension));
62
63    let cli = bin_cli::args::Cli::parse();
64    bin_cli::helpers::init_tracing(cli.verbose, cli.quiet, cli.no_color);
65
66    if cli.jobs > 0 {
67        rayon::ThreadPoolBuilder::new()
68            .num_threads(cli.jobs)
69            .build_global()
70            .ok();
71    }
72
73    #[cfg(feature = "dylib-loader")]
74    match extensions::dylib::load_dylib_extensions_from_config(&cli.config) {
75        Ok(mut dylib_extensions) => extensions.append(&mut dylib_extensions),
76        Err(e) => {
77            eprintln!("error: {e:#}");
78            return std::process::ExitCode::FAILURE;
79        }
80    }
81
82    let _ = EXTENSIONS.set(extensions);
83
84    match bin_cli::dispatch::run(cli) {
85        Ok(()) => std::process::ExitCode::SUCCESS,
86        Err(e) => {
87            eprintln!("error: {e:#}");
88            std::process::ExitCode::FAILURE
89        }
90    }
91}
92
93/// Active extensions for the current pipeline run.
94///
95/// Populated by [`run_with_extensions`] before dispatch; accessed by
96/// pipeline stages via [`with_extensions`]. Process-global (not
97/// `thread_local!`) so rayon worker threads see the same list.
98pub(crate) static EXTENSIONS: std::sync::OnceLock<Vec<Box<dyn Extension>>> = std::sync::OnceLock::new();
99
100/// Run `f` with an immutable reference to the active extensions list.
101pub(crate) fn with_extensions<F, R>(f: F) -> R
102where
103    F: FnOnce(&[Box<dyn Extension>]) -> R,
104{
105    static EMPTY: Vec<Box<dyn Extension>> = Vec::new();
106    f(EXTENSIONS.get().unwrap_or(&EMPTY))
107}