Skip to main content

bytes_radar/cli/
mod.rs

1#[cfg(feature = "cli")]
2mod args;
3#[cfg(feature = "cli")]
4mod output;
5#[cfg(feature = "cli")]
6mod progress;
7#[cfg(feature = "cli")]
8mod url_parser;
9
10#[cfg(feature = "cli")]
11use crate::core::*;
12#[cfg(feature = "cli")]
13use crate::net::RemoteAnalyzer;
14#[cfg(feature = "cli")]
15use clap::Parser;
16#[cfg(feature = "cli")]
17use std::time::Instant;
18
19#[cfg(feature = "cli")]
20pub use args::{Cli, OutputFormat};
21
22#[cfg(feature = "cli")]
23pub async fn run() -> Result<()> {
24    let cli = args::Cli::parse();
25
26    init_logging(&cli)?;
27
28    let mut cli = cli;
29    if let Ok(token) = std::env::var("BRADAR_TOKEN") {
30        if cli.token.is_none() {
31            cli.token = Some(token);
32        }
33    }
34
35    match &cli.url {
36        Some(url) => analyze_remote_archive(url, &cli).await,
37        None => {
38            url_parser::show_usage_examples();
39            std::process::exit(1);
40        }
41    }
42}
43
44#[cfg(feature = "cli")]
45fn init_logging(cli: &Cli) -> Result<()> {
46    let log_level = if cli.trace {
47        "trace"
48    } else if cli.debug {
49        "debug"
50    } else {
51        "warn"
52    };
53
54    let mut builder =
55        env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(log_level));
56
57    if let Some(log_file) = &cli.log_file {
58        use std::fs::OpenOptions;
59
60        let target = Box::new(
61            OpenOptions::new()
62                .create(true)
63                .append(true)
64                .open(log_file)
65                .map_err(|e| crate::core::error::AnalysisError::file_read(log_file, e))?,
66        );
67        builder.target(env_logger::Target::Pipe(target));
68    }
69
70    builder.init();
71    Ok(())
72}
73
74#[cfg(feature = "cli")]
75async fn analyze_remote_archive(url: &str, cli: &Cli) -> Result<()> {
76    let should_show_progress =
77        !cli.no_progress && matches!(cli.format, OutputFormat::Table) && !cli.quiet;
78
79    let processed_url = url_parser::expand_url(url);
80
81    if should_show_progress && !cli.quiet {
82        println!("Analyzing: {}", processed_url);
83    }
84
85    let start_time = Instant::now();
86    let progress_bar = progress::create_progress_bar(should_show_progress);
87
88    let mut analyzer = RemoteAnalyzer::new();
89
90    if let Some(token) = &cli.token {
91        let mut credentials = std::collections::HashMap::new();
92        credentials.insert("token".to_string(), token.clone());
93        analyzer.set_provider_credentials("github", credentials);
94    }
95
96    analyzer.set_timeout(cli.timeout);
97    analyzer.set_allow_insecure(cli.allow_insecure);
98
99    if let Some(pb) = progress_bar.clone() {
100        analyzer.set_progress_hook(progress::ProgressBarHook::new(pb));
101    }
102
103    configure_analyzer_filters(&mut analyzer, cli)?;
104
105    let project_analysis = analyzer.analyze_url(&processed_url).await?;
106
107    let elapsed = start_time.elapsed();
108
109    if let Some(pb) = &progress_bar {
110        pb.finish_and_clear();
111    }
112
113    progress::show_completion_message(elapsed, cli.quiet);
114
115    output_results(&project_analysis, cli)?;
116
117    Ok(())
118}
119
120#[cfg(feature = "cli")]
121fn configure_analyzer_filters(analyzer: &mut RemoteAnalyzer, cli: &Cli) -> Result<()> {
122    if cli.aggressive_filter {
123        analyzer.set_aggressive_filtering(true);
124    } else {
125        let filter = filter::IntelligentFilter {
126            max_file_size: cli.max_file_size * 1024,
127            ignore_test_dirs: !cli.include_tests,
128            ignore_docs_dirs: !cli.include_docs,
129            ..filter::IntelligentFilter::default()
130        };
131
132        analyzer.set_filter(filter);
133    }
134
135    Ok(())
136}
137
138#[cfg(feature = "cli")]
139fn output_results(project_analysis: &analysis::ProjectAnalysis, cli: &Cli) -> Result<()> {
140    match cli.format {
141        OutputFormat::Table => {
142            output::print_table_format(project_analysis, cli.detailed, cli.quiet);
143        }
144        OutputFormat::Json => output::print_json_format(project_analysis)?,
145        OutputFormat::Csv => output::print_csv_format(project_analysis)?,
146        OutputFormat::Xml => output::print_xml_format(project_analysis)?,
147        OutputFormat::Yaml => output::print_yaml_format(project_analysis)?,
148        OutputFormat::Toml => output::print_toml_format(project_analysis)?,
149    }
150
151    Ok(())
152}