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