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}