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
21pub mod adapters;
22pub mod backends;
23pub mod bin_cli;
24pub mod cli;
25pub mod codegen;
26pub mod core;
27pub mod docs;
28pub mod e2e;
29pub mod extensions;
30pub mod extract;
31pub mod publish;
32pub mod readme;
33pub mod scaffold;
34pub mod snippets;
35
36pub use core::extension::{Extension, ExtensionConfig};
37pub use core::template_env::TemplateEnv;
38pub use extensions::template::TemplateExtension;
39
40/// Run the alef CLI, threading the given extensions through the pipeline.
41///
42/// The built-in [`TemplateExtension`] is always prepended so consumers who
43/// pass `vec![]` still get `[[extensions.template]]` block support.
44///
45/// # Example
46///
47/// ```rust,no_run
48/// fn main() -> std::process::ExitCode {
49///     alef::run_with_extensions(vec![])
50/// }
51/// ```
52pub fn run_with_extensions(mut extensions: Vec<Box<dyn Extension>>) -> std::process::ExitCode {
53    use clap::Parser;
54
55    // Always prepend the built-in TemplateExtension.
56    extensions.insert(0, Box::new(TemplateExtension));
57
58    let cli = bin_cli::args::Cli::parse();
59    bin_cli::helpers::init_tracing(cli.verbose, cli.quiet, cli.no_color);
60
61    if cli.jobs > 0 {
62        rayon::ThreadPoolBuilder::new()
63            .num_threads(cli.jobs)
64            .build_global()
65            .ok();
66    }
67
68    // Store extensions in a process-global so the pipeline can access them
69    // from rayon worker threads (which have their own thread-locals). A
70    // `thread_local!` here would leave the workers seeing an empty list —
71    // every parallel `generate()` call would then skip extension emission.
72    let _ = EXTENSIONS.set(extensions);
73
74    match bin_cli::dispatch::run(cli) {
75        Ok(()) => std::process::ExitCode::SUCCESS,
76        Err(e) => {
77            eprintln!("error: {e:#}");
78            std::process::ExitCode::FAILURE
79        }
80    }
81}
82
83/// Active extensions for the current pipeline run.
84///
85/// Populated by [`run_with_extensions`] before dispatch; accessed by
86/// pipeline stages via [`with_extensions`]. Process-global (not
87/// `thread_local!`) so rayon worker threads see the same list.
88pub(crate) static EXTENSIONS: std::sync::OnceLock<Vec<Box<dyn Extension>>> = std::sync::OnceLock::new();
89
90/// Run `f` with an immutable reference to the active extensions list.
91pub(crate) fn with_extensions<F, R>(f: F) -> R
92where
93    F: FnOnce(&[Box<dyn Extension>]) -> R,
94{
95    static EMPTY: Vec<Box<dyn Extension>> = Vec::new();
96    f(EXTENSIONS.get().unwrap_or(&EMPTY))
97}