use clap::{Parser, Subcommand};
use guicons_cli::{AddError, FetchSummary};
use std::path::PathBuf;
use std::process::ExitCode;
#[derive(Parser)]
#[command(name = "icons")]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
Fetch {
#[arg(long, default_value = "icons.gui.toml")]
manifest: PathBuf,
#[arg(long)]
force: bool,
},
Update {
#[arg(long, default_value = "icons.gui.toml")]
manifest: PathBuf,
},
Check {
#[arg(long, default_value = "icons.gui.toml")]
manifest: PathBuf,
},
Add {
source: String,
#[arg(long)]
name: Option<String>,
#[arg(long, value_delimiter = ',')]
variants: Vec<String>,
#[arg(long)]
size: Option<u16>,
#[arg(long, default_value = "icons.gui.toml")]
manifest: PathBuf,
#[arg(long)]
force: bool,
},
}
fn main() -> ExitCode {
let _ = miette::set_hook(Box::new(|_| {
Box::new(
miette::MietteHandlerOpts::new()
.force_graphical(true)
.color(std::env::var_os("NO_COLOR").is_none())
.build(),
)
}));
let cli = Cli::parse();
match cli.command {
Command::Fetch { manifest, force } => run_fetch(&manifest, force),
Command::Update { manifest } => run_fetch(&manifest, true),
Command::Check { manifest } => run_check(&manifest),
Command::Add { source, name, variants, size, manifest, force } => {
run_add(&manifest, &source, name.as_deref(), &variants, size, force)
}
}
}
fn run_check(manifest: &PathBuf) -> ExitCode {
let (entry_count, reports) = guicons_cli::check(manifest);
if reports.is_empty() {
println!("\u{2713} {} - {entry_count} icon{} OK", manifest.display(), if entry_count == 1 { "" } else { "s" });
return ExitCode::SUCCESS;
}
let (errors, notes): (Vec<_>, Vec<_>) =
reports.into_iter().partition(|report| report.severity() == Some(miette::Severity::Error));
for report in errors.iter().chain(¬es) {
eprintln!("{report:?}");
}
if !notes.is_empty() {
eprintln!("{} note{} found", notes.len(), if notes.len() == 1 { "" } else { "s" });
}
if errors.is_empty() {
println!("\u{2713} {} - {entry_count} icon{} OK", manifest.display(), if entry_count == 1 { "" } else { "s" });
return ExitCode::SUCCESS;
}
eprintln!("{} error{} found", errors.len(), if errors.len() == 1 { "" } else { "s" });
ExitCode::FAILURE
}
fn run_add(
manifest: &PathBuf,
source: &str,
name: Option<&str>,
variants: &[String],
size: Option<u16>,
force: bool,
) -> ExitCode {
match guicons_cli::add(manifest, source, name, variants, size, force) {
Ok(keys) => {
for key in keys {
println!("added {key}");
}
ExitCode::SUCCESS
}
Err(AddError::Manifest(errors)) => {
eprintln!("existing manifest has errors:");
for error in errors {
eprintln!(" {error}");
}
ExitCode::FAILURE
}
Err(AddError::AlreadyExists(keys)) => {
eprintln!("already in manifest (use --force to overwrite): {}", keys.join(", "));
ExitCode::FAILURE
}
Err(AddError::Plan(message)) => {
eprintln!("{message}");
ExitCode::FAILURE
}
Err(AddError::Io(message)) => {
eprintln!("{message}");
ExitCode::FAILURE
}
Err(AddError::InvalidResult(errors)) => {
eprintln!("refusing to write - result would not parse:");
for error in errors {
eprintln!(" {error}");
}
ExitCode::FAILURE
}
}
}
fn run_fetch(manifest: &PathBuf, force: bool) -> ExitCode {
let cwd = match std::env::current_dir() {
Ok(cwd) => cwd,
Err(e) => {
eprintln!("failed to read current directory: {e}");
return ExitCode::FAILURE;
}
};
match guicons_cli::fetch(manifest, &cwd, force) {
Ok(summary) => {
print_summary(&summary);
if summary.is_success() {
ExitCode::SUCCESS
} else {
ExitCode::FAILURE
}
}
Err(errors) => {
for error in errors {
eprintln!("{error}");
}
ExitCode::FAILURE
}
}
}
fn print_summary(summary: &FetchSummary) {
for id in &summary.fetched {
println!("fetched {id}");
}
for id in &summary.skipped {
println!("cached {id}");
}
for (id, error) in &summary.failed {
eprintln!("failed {id}: {error}");
}
println!(
"{} fetched, {} already cached, {} failed",
summary.fetched.len(),
summary.skipped.len(),
summary.failed.len()
);
}