arborist-cli 0.2.0

CLI for arborist-metrics: cognitive/cyclomatic complexity and SLOC metrics
Documentation
pub mod about;
pub mod analysis;
pub mod cli;
pub mod error;
pub mod output;
pub mod traversal;
pub mod update;

use cli::AnalyzeArgs;
use error::{ArboristError, ExitReport};

pub fn run(args: &AnalyzeArgs) -> Result<ExitReport, ArboristError> {
    let is_stdin = args.paths.is_empty() && atty::is(atty::Stream::Stdin).not_tty();

    if is_stdin {
        let language = args.language.as_deref().ok_or(ArboristError::NoLanguage)?;
        let config = analysis::build_config(args);
        let report = analysis::analyze_stdin(language, &config)?;
        let reports = vec![report];
        let (reports, threshold_exceeded) = analysis::apply_filters(&reports, args);
        output::write_output(&reports, args, threshold_exceeded)?;
        return Ok(ExitReport {
            threshold_exceeded,
            had_errors: false,
        });
    }

    if args.paths.is_empty() {
        return Err(ArboristError::InvalidArgument(
            "no files or directories specified and stdin is not a pipe".into(),
        ));
    }

    let config = analysis::build_config(args);
    let (reports, errors) = analysis::analyze_paths(&args.paths, &config, args)?;

    for err in &errors {
        eprintln!("warning: {err}");
    }

    let (filtered, threshold_exceeded) = analysis::apply_filters(&reports, args);
    output::write_output(&filtered, args, threshold_exceeded)?;

    Ok(ExitReport {
        threshold_exceeded,
        had_errors: !errors.is_empty(),
    })
}

mod atty {
    pub enum Stream {
        Stdin,
    }

    pub struct AttyResult(bool);

    impl AttyResult {
        pub fn not_tty(self) -> bool {
            !self.0
        }
    }

    pub fn is(stream: Stream) -> AttyResult {
        match stream {
            Stream::Stdin => {
                use std::io::IsTerminal;
                AttyResult(std::io::stdin().is_terminal())
            }
        }
    }
}