use std::path::PathBuf;
use anyhow::{Context, Result};
use clap::{Parser, Subcommand, crate_version};
use dover::{Diff, GitChange, HTML_BOILERPLATE, Html, Overview, Treeish};
#[derive(Debug, Parser)]
#[command(author, version = crate_version!(), about = "Diff OVERview - summarize git diffs of Rust code")]
struct Cli {
#[arg(long, global = true, default_value_t = false)]
to_html: bool,
#[command(subcommand)]
command: Command,
}
#[derive(Copy, Clone)]
enum OutputFormat {
Html,
Plain,
}
impl OutputFormat {
fn new(to_html: bool) -> Self {
if to_html {
OutputFormat::Html
} else {
OutputFormat::Plain
}
}
}
#[derive(Debug, Subcommand)]
enum Command {
Diff {
commit1: Option<String>,
commit2: Option<String>,
},
Files { file1: PathBuf, file2: PathBuf },
}
fn main() -> Result<()> {
let args = Cli::parse();
let output = OutputFormat::new(args.to_html);
match args.command {
Command::Diff { commit1, commit2 } => run_diff(Command::Diff { commit1, commit2 }, output),
Command::Files { file1, file2 } => run_files(Command::Files { file1, file2 }, output),
}
}
fn run_diff(command: Command, output: OutputFormat) -> Result<()> {
let Command::Diff { commit1, commit2 } = command else {
unreachable!();
};
let trees = commit1.map(|c1| Treeish::new(c1, commit2));
let repo_changes = dover::get_changed_files(PathBuf::from("."), trees)?;
let changes = repo_changes
.changed_files
.into_iter()
.filter(|c| c.path.extension().is_some_and(|ext| ext == "rs"));
let mut html = HTML_BOILERPLATE.to_string();
for changed_file in changes {
let path = changed_file.path;
match changed_file.change_type {
GitChange::Modified {
before_contents,
after_contents,
} => {
let overview1 = Overview::try_from((path.clone(), before_contents))
.context("Error getting overview")?;
let overview2 =
Overview::try_from((path, after_contents)).context("Error getting overview")?;
let overview_diff = overview1.diff_with(&overview2);
if !overview_diff.all_empty() {
match output {
OutputFormat::Plain => println!("{overview_diff}"),
OutputFormat::Html => html.push_str(&overview_diff.to_html()),
}
}
}
GitChange::Added { contents } => {
let overview1 = Overview::try_from((path.clone(), "".to_string()))
.context("Error getting overview")?;
let overview2 =
Overview::try_from((path, contents)).context("Error getting overview")?;
let overview_diff = overview1.diff_with(&overview2);
if !overview_diff.all_empty() {
match output {
OutputFormat::Plain => println!("{overview_diff}"),
OutputFormat::Html => html.push_str(&overview_diff.to_html()),
}
}
}
GitChange::Deleted { contents } => {
let overview1 = Overview::try_from((path.clone(), contents))
.context("Error getting overview")?;
let overview2 =
Overview::try_from((path, "".to_string())).context("Error getting overview")?;
let overview_diff = overview1.diff_with(&overview2);
if !overview_diff.all_empty() {
match output {
OutputFormat::Plain => println!("{overview_diff}"),
OutputFormat::Html => html.push_str(&overview_diff.to_html()),
}
}
}
}
}
if let OutputFormat::Html = output {
html.push_str("</body></html>");
println!("{}", html);
}
Ok(())
}
fn run_files(c: Command, output: OutputFormat) -> Result<()> {
let Command::Files { file1, file2 } = c else {
unreachable!();
};
let overview1 = Overview::try_from(file1).context("Error getting overview for file1")?;
let overview2 = Overview::try_from(file2).context("Error getting overview for file2")?;
let file_diff = overview1.diff_with(&overview2);
match output {
OutputFormat::Plain => println!("{}", file_diff),
OutputFormat::Html => {
let mut html = HTML_BOILERPLATE.to_string();
html.push_str(&file_diff.to_html());
html.push_str("</body></html>");
println!("{}", html);
}
}
Ok(())
}
#[allow(unused)]
fn run_overview(c: Command) -> Result<()> {
todo!()
}