#![warn(missing_docs)]
mod opts;
use clap::{crate_name, crate_version, Parser};
use env_logger::Env;
use opts::*;
use prdoclib::{
commands::{
check::{CheckCmd, CheckResult},
generate::GenerateCmd,
load::LoadCmd,
scan::ScanCmd,
version::VersionCmd,
},
common::PRNumber,
config::Config,
schema::Schema,
};
use std::{collections::HashSet, env, path::PathBuf};
fn main() -> color_eyre::Result<()> {
env_logger::Builder::from_env(Env::default().default_filter_or("none")).init();
color_eyre::install()?;
let opts: Opts = Opts::parse();
log::debug!("opts: {opts:#?}");
let config = match Config::load(opts.config) {
Ok(c) => {
log::debug!("Config found: {:#?}", c);
c
}
Err(_e) => {
log::warn!("No config could be found, using default");
Config::get_default_config()
}
};
let prdoc_dir: Vec<PathBuf> = match (&config.prdoc_folders, opts.prdoc_folders) {
(config_list, None) => config_list.clone(),
(_config_list, Some(args)) => vec![args],
};
log::debug!("prdoc_dir: {:#?}", prdoc_dir);
match opts.subcmd {
Some(SubCommand::Generate(cmd_opts)) => {
log::debug!("cmd_opts: {cmd_opts:#?}");
let dir = prdoclib::utils::get_pr_doc_folder(cmd_opts.output_dir, &config);
let template_path = prdoclib::utils::get_template_path(&config);
log::debug!("PRDoc folder: {dir:?}");
match GenerateCmd::run(cmd_opts.dry_run, cmd_opts.number, None, Some(dir), template_path) {
Ok(_) => Ok(()),
Err(e) => {
eprint!("{e:?}");
std::process::exit(exitcode::IOERR);
}
}
}
Some(SubCommand::Check(cmd_opts)) => {
log::debug!("cmd_opts: {cmd_opts:#?}");
let results: HashSet<CheckResult> = prdoc_dir
.iter()
.flat_map(|d| {
CheckCmd::run(
&config,
cmd_opts.schema.clone(),
d,
cmd_opts.file.clone(),
cmd_opts.number.clone(),
cmd_opts.list.clone(),
)
.unwrap()
})
.collect();
if !opts.json {
for (src, result) in &results {
let pr_number: PRNumber = src.into();
println!("PR #{pr_number: <4} -> {}", if *result { "OK " } else { "ERR" });
}
} else {
let json = serde_json::to_string_pretty(&results).expect("We can serialize the result");
println!("{json}");
}
let all_good = results.iter().map(|(_number, res)| res).all(|&res| res);
if all_good {
std::process::exit(exitcode::OK)
} else {
std::process::exit(exitcode::DATAERR)
}
}
Some(SubCommand::Scan(cmd_opts)) => {
let schema_path = config.schema_path();
let schema = Schema::new(schema_path);
log::debug!("cmd_opts: {cmd_opts:#?}");
let files = ScanCmd::run(schema.clone(), prdoc_dir, cmd_opts.all);
let load_cmd = LoadCmd::new(schema);
let mut res: Vec<(Option<PRNumber>, PathBuf)> = files
.iter()
.map(|f| {
let prdoc = load_cmd.load_file(f);
let n = match prdoc {
Ok(p) => Some(p.doc_filename.number),
Err(_) => None,
};
(n, f.clone())
})
.collect();
if cmd_opts.sort {
res.sort_by(|a, b| a.1.cmp(&b.1));
}
if opts.json {
println!("{}", serde_json::to_string_pretty(&res).unwrap());
} else {
res.iter().for_each(|(n, f)| {
println!(
"{number}\t{file}",
file = f.display(),
number = if let Some(number) = n { number.to_string() } else { "n/a".to_string() }
);
});
}
Ok(())
}
Some(SubCommand::Load(cmd_opts)) => {
log::debug!("cmd_opts: {cmd_opts:#?}");
let result = prdoc_dir
.iter()
.map(|dir| {
LoadCmd::run(&config, dir, cmd_opts.file.clone(), cmd_opts.number.clone(), cmd_opts.list.clone())
.unwrap()
})
.fold((true, HashSet::new()), |(acc_status, acc_wrapper), (status, wrapper)| {
let mut new_wrapper = acc_wrapper;
new_wrapper.extend(wrapper);
(acc_status && status, new_wrapper)
});
if opts.json {
println!("{}", serde_json::to_string_pretty(&result.1).unwrap());
} else {
println!("{}", serde_yaml::to_string(&result.1).unwrap());
}
if result.0 {
std::process::exit(exitcode::OK);
} else {
std::process::exit(exitcode::DATAERR)
}
}
None => {
if opts.version {
let name = crate_name!();
let version = crate_version!();
VersionCmd::run(name, version, opts.json);
Ok(())
} else {
unreachable!("We show help if there is no arg");
}
}
}
}