yerba 0.8.1

YAML Editing and Refactoring with Better Accuracy
use std::process;
use std::sync::LazyLock;

use indoc::indoc;

use super::colorize_examples;
use super::{output, parse_file, resolve_files};

static EXAMPLES: LazyLock<String> = LazyLock::new(|| {
  colorize_examples(indoc! {r#"
    yerba unique config.yml "tags"
    yerba unique videos.yml --by ".id"
    yerba unique speakers.yml --by ".name"
    yerba unique speakers.yml --by ".name" --remove
  "#})
});

#[derive(clap::Args)]
#[command(
  about = "Find or remove duplicate items in a sequence",
  arg_required_else_help = true,
  after_help = EXAMPLES.as_str()
)]
pub struct Args {
  file: String,
  /// Selector (optional — omit for root-level sequence)
  selector: Option<String>,
  /// Field to check for uniqueness (omit for scalar arrays)
  #[arg(long)]
  by: Option<String>,
  /// Remove duplicate items (default: report only)
  #[arg(long)]
  remove: bool,
  #[arg(long)]
  dry_run: bool,
}

impl Args {
  pub fn run(self) {
    use super::ui;

    let selector = self.selector.as_deref().unwrap_or("");
    let by = self.by.as_deref().unwrap_or(".");

    for resolved_file in resolve_files(&self.file) {
      let mut document = parse_file(&resolved_file);

      match document.unique(selector, by, self.remove) {
        Ok(duplicates) => {
          let noun = if duplicates.len() == 1 { "duplicate" } else { "duplicates" };

          if duplicates.is_empty() {
            eprintln!("{} in {}", ui::success("No duplicates found"), resolved_file);
          } else if self.remove {
            eprintln!("{} from {}", ui::pending(format!("Removed {} {noun}", duplicates.len())), resolved_file);

            for duplicate in &duplicates {
              eprintln!("  {}", ui::subtle(format!("line {}: {} == {}", duplicate.line, by, duplicate.value)));
            }

            output(&resolved_file, &document, self.dry_run);
          } else {
            eprintln!("{} in {}", ui::failure(format!("Found {} {noun}", duplicates.len())), resolved_file);

            for duplicate in &duplicates {
              eprintln!("  {}", ui::subtle(format!("line {}: {} == {}", duplicate.line, by, duplicate.value)));
            }

            process::exit(1);
          }
        }

        Err(error) => {
          eprintln!("{} {}", ui::failure("Error:"), error);
          process::exit(1);
        }
      }
    }
  }
}