1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//! Command line interface entry points and utilities

mod generate;
mod query;
mod splice;
mod vendor;

use clap::Parser;
use tracing::{Level, Subscriber};
use tracing_subscriber::fmt::format::{Format, Full};
use tracing_subscriber::fmt::time::SystemTime;
use tracing_subscriber::fmt::{FormatEvent, FormatFields};
use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::FmtSubscriber;

pub use self::generate::GenerateOptions;
pub use self::query::QueryOptions;
pub use self::splice::SpliceOptions;
pub use self::vendor::VendorOptions;

// Entrypoints
pub use generate::generate;
pub use query::query;
pub use splice::splice;
pub use vendor::vendor;

#[derive(Parser, Debug)]
#[clap(
    name = "cargo-bazel",
    about = "crate_universe` is a collection of tools which use Cargo to generate build targets for Bazel.",
    version
)]
pub enum Options {
    /// Generate Bazel Build files from a Cargo manifest.
    Generate(GenerateOptions),

    /// Splice together disjoint Cargo and Bazel info into a single Cargo workspace manifest.
    Splice(SpliceOptions),

    /// Query workspace info to determine whether or not a repin is needed.
    Query(QueryOptions),

    /// Vendor BUILD files to the workspace with either repository definitions or `cargo vendor` generated sources.
    Vendor(VendorOptions),
}

// Convenience wrappers to avoid dependencies in the binary
pub type Result<T> = anyhow::Result<T>;

pub fn parse_args() -> Options {
    Options::parse()
}

const EXPECTED_LOGGER_NAMES: [&str; 4] = ["Generate", "Splice", "Query", "Vendor"];

/// A wrapper for the tracing-subscriber default [FormatEvent]
/// that prepends the name of the active CLI option.
struct LoggingFormatEvent {
    name: String,
    base: Format<Full, SystemTime>,
}

impl<S, N> FormatEvent<S, N> for LoggingFormatEvent
where
    S: Subscriber + for<'a> LookupSpan<'a>,
    N: for<'a> FormatFields<'a> + 'static,
{
    fn format_event(
        &self,
        ctx: &tracing_subscriber::fmt::FmtContext<'_, S, N>,
        mut writer: tracing_subscriber::fmt::format::Writer<'_>,
        event: &tracing::Event<'_>,
    ) -> std::fmt::Result {
        write!(writer, "{} ", self.name)?;
        self.base.format_event(ctx, writer, event)
    }
}

impl LoggingFormatEvent {
    fn new(name: &str) -> Self {
        Self {
            name: name.to_owned(),
            base: Format::default(),
        }
    }
}

/// Initialize logging for one of the cli options.
pub fn init_logging(name: &str) {
    if !EXPECTED_LOGGER_NAMES.contains(&name) {
        panic!(
            "Unexpected logger name {}, use of one of {:?}",
            name, EXPECTED_LOGGER_NAMES
        );
    }

    // a builder for `FmtSubscriber`.
    let subscriber = FmtSubscriber::builder()
        // all spans/events with a level higher than TRACE (e.g, debug, info, warn, etc.)
        // will be written to stdout.
        .with_max_level(
            std::env::var("CARGO_BAZEL_DEBUG")
                .map(|_| Level::DEBUG)
                .unwrap_or(Level::INFO),
        )
        .event_format(LoggingFormatEvent::new(name))
        // completes the builder.
        .finish();

    tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
}