use std::io::IsTerminal;
use std::process::ExitCode;
use anyhow::Result;
use clap::{Args, value_parser};
use codespan_reporting::term::termcolor::StandardStream;
use rpm_spec::printer::print_to;
use rpm_spec_analyzer::parse;
use crate::app::ColorChoice;
use crate::commands::{MAX_INDENT_LEVEL, printer_config};
use crate::config as cli_config;
use crate::io;
use crate::output::pretty::{AnsiWriter, Theme};
use crate::output::resolve_color;
const DEFAULT_PRETTY_INDENT: usize = 2;
#[derive(Debug, Args)]
pub struct Cmd {
#[command(flatten)]
pub input: crate::app::CommonInput,
#[arg(long)]
pub preamble_align_column: Option<u32>,
#[arg(long, value_parser = value_parser!(u32).range(0..=MAX_INDENT_LEVEL as i64))]
pub indent: Option<u32>,
}
impl Cmd {
pub fn run(self, color: ColorChoice) -> Result<ExitCode> {
let sources = io::read_sources(&self.input.paths)?;
let mut config_cache = cli_config::ConfigCache::new(self.input.config.clone());
let stream =
StandardStream::stdout(resolve_color(color, || std::io::stdout().is_terminal()));
let mut writer = stream.lock();
let mut sink = AnsiWriter::new(&mut writer, Theme::dark());
let mut any_io_error = false;
for source in sources {
let Some(analyzer_cfg) = config_cache.load_or_report(&source.path, &mut any_io_error)
else {
continue;
};
let mut pcfg = printer_config::apply_overrides(
&analyzer_cfg.format,
self.preamble_align_column,
self.indent,
);
if self.indent.is_none() && pcfg.indent == 0 {
pcfg = pcfg.with_indent(DEFAULT_PRETTY_INDENT);
}
let outcome = parse(&source.contents);
print_to(&outcome.spec, &pcfg, &mut sink);
if sink.has_broken_pipe() {
tracing::debug!(
path = %source.display_name(),
"broken pipe on stdout; downstream consumer closed early"
);
let _ = sink.take_error();
break;
}
}
if let Some(e) = sink.take_error() {
eprintln!("error writing pretty output: {e:#}");
return Ok(ExitCode::from(2));
}
Ok(if any_io_error {
ExitCode::from(2)
} else {
ExitCode::SUCCESS
})
}
}