cargo_bazel/
cli.rs

1//! Command line interface entry points and utilities
2
3mod generate;
4mod query;
5mod render;
6mod splice;
7mod vendor;
8
9use clap::Parser;
10use tracing::Subscriber;
11use tracing_subscriber::fmt::format::{Format, Full};
12use tracing_subscriber::fmt::time::SystemTime;
13use tracing_subscriber::fmt::{FormatEvent, FormatFields};
14use tracing_subscriber::registry::LookupSpan;
15use tracing_subscriber::FmtSubscriber;
16
17pub use tracing::Level as LogLevel;
18
19pub use self::generate::GenerateOptions;
20pub use self::query::QueryOptions;
21pub use self::render::RenderOptions;
22pub use self::splice::SpliceOptions;
23pub use self::vendor::VendorOptions;
24
25// Entrypoints
26pub use generate::generate;
27pub use query::query;
28pub use render::render;
29pub use splice::splice;
30pub use vendor::vendor;
31
32#[derive(Parser, Debug)]
33#[clap(
34    name = "cargo-bazel",
35    about = "crate_universe` is a collection of tools which use Cargo to generate build targets for Bazel.",
36    version
37)]
38pub enum Options {
39    /// Generate Bazel Build files from a Cargo manifest.
40    Generate(GenerateOptions),
41
42    /// Splice together disjoint Cargo and Bazel info into a single Cargo workspace manifest.
43    Splice(SpliceOptions),
44
45    /// Query workspace info to determine whether or not a repin is needed.
46    Query(QueryOptions),
47
48    /// Vendor BUILD files to the workspace with either repository definitions or `cargo vendor` generated sources.
49    Vendor(VendorOptions),
50
51    /// Render a BUILD file for a single crate.
52    Render(RenderOptions),
53}
54
55// Convenience wrappers to avoid dependencies in the binary
56pub type Result<T> = anyhow::Result<T>;
57
58pub fn parse_args() -> Options {
59    Options::parse()
60}
61
62const EXPECTED_LOGGER_NAMES: [&str; 5] = ["Generate", "Splice", "Query", "Vendor", "Render"];
63
64/// A wrapper for the tracing-subscriber default [FormatEvent]
65/// that prepends the name of the active CLI option.
66struct LoggingFormatEvent {
67    name: String,
68    base: Format<Full, SystemTime>,
69}
70
71impl<S, N> FormatEvent<S, N> for LoggingFormatEvent
72where
73    S: Subscriber + for<'a> LookupSpan<'a>,
74    N: for<'a> FormatFields<'a> + 'static,
75{
76    fn format_event(
77        &self,
78        ctx: &tracing_subscriber::fmt::FmtContext<'_, S, N>,
79        mut writer: tracing_subscriber::fmt::format::Writer<'_>,
80        event: &tracing::Event<'_>,
81    ) -> std::fmt::Result {
82        write!(writer, "{} ", self.name)?;
83        self.base.format_event(ctx, writer, event)
84    }
85}
86
87impl LoggingFormatEvent {
88    fn new(name: &str) -> Self {
89        Self {
90            name: name.to_owned(),
91            base: Format::default(),
92        }
93    }
94}
95
96/// Initialize logging for one of the cli options.
97pub fn init_logging(name: &str, level: LogLevel) {
98    if !EXPECTED_LOGGER_NAMES.contains(&name) {
99        panic!(
100            "Unexpected logger name {}, use of one of {:?}",
101            name, EXPECTED_LOGGER_NAMES
102        );
103    }
104
105    let subscriber = FmtSubscriber::builder()
106        .with_max_level(level)
107        .event_format(LoggingFormatEvent::new(name))
108        .finish();
109
110    tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
111}