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