use clap::Args;
use std::path::PathBuf;
#[derive(Args, Debug)]
pub struct DiffArgs {
#[arg(value_name = "FILE")]
pub source: PathBuf,
#[arg(value_name = "FILE")]
pub target: Option<PathBuf>,
#[arg(short, long, default_value = "text", value_name = "FORMAT")]
pub format: DiffFormat,
#[arg(long, value_name = "LIST", value_delimiter = ',')]
pub tables: Option<Vec<String>>,
#[arg(long)]
pub summary_only: bool,
#[arg(long)]
pub include_unchanged: bool,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum DiffFormat {
#[default]
Text,
Json,
Summary,
}
impl std::str::FromStr for DiffFormat {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"text" => Ok(DiffFormat::Text),
"json" => Ok(DiffFormat::Json),
"summary" => Ok(DiffFormat::Summary),
_ => Err(format!(
"Invalid format '{}'. Valid options: text, json, summary",
s
)),
}
}
}
impl std::fmt::Display for DiffFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DiffFormat::Text => write!(f, "text"),
DiffFormat::Json => write!(f, "json"),
DiffFormat::Summary => write!(f, "summary"),
}
}
}
impl DiffArgs {
pub fn is_two_file_diff(&self) -> bool {
self.target.is_some()
}
pub fn should_include_table(&self, table_name: &str) -> bool {
match &self.tables {
Some(tables) => tables.iter().any(|t| t == table_name),
None => true,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_diff_format_parse() {
assert_eq!("text".parse::<DiffFormat>().unwrap(), DiffFormat::Text);
assert_eq!("json".parse::<DiffFormat>().unwrap(), DiffFormat::Json);
assert_eq!(
"summary".parse::<DiffFormat>().unwrap(),
DiffFormat::Summary
);
assert_eq!("JSON".parse::<DiffFormat>().unwrap(), DiffFormat::Json);
assert!("invalid".parse::<DiffFormat>().is_err());
}
#[test]
fn test_diff_args_table_filter() {
let args = DiffArgs {
source: PathBuf::from("test.json"),
target: None,
format: DiffFormat::Text,
tables: Some(vec!["tasks".to_string(), "dependencies".to_string()]),
summary_only: false,
include_unchanged: false,
};
assert!(args.should_include_table("tasks"));
assert!(args.should_include_table("dependencies"));
assert!(!args.should_include_table("attachments"));
}
#[test]
fn test_diff_args_no_filter() {
let args = DiffArgs {
source: PathBuf::from("test.json"),
target: None,
format: DiffFormat::Text,
tables: None,
summary_only: false,
include_unchanged: false,
};
assert!(args.should_include_table("tasks"));
assert!(args.should_include_table("attachments"));
}
}