1extern crate radicle_surf;
2
3use std::{env::Args, str::FromStr, time::Instant};
4
5use radicle_git_ext::Oid;
6use radicle_surf::{diff::Diff, Repository};
7
8fn main() {
9 let options = get_options_or_exit();
10 let repo = init_repository_or_exit(&options.path_to_repo);
11 let head_oid = match options.head_revision {
12 HeadRevision::Head => repo.head().unwrap(),
13 HeadRevision::Commit(id) => Oid::from_str(&id).unwrap(),
14 };
15 let base_oid = Oid::from_str(&options.base_revision).unwrap();
16 let now = Instant::now();
17 let elapsed_nanos = now.elapsed().as_nanos();
18 let diff = repo.diff(base_oid, head_oid).unwrap();
19 print_diff_summary(&diff, elapsed_nanos);
20}
21
22fn get_options_or_exit() -> Options {
23 match Options::parse(std::env::args()) {
24 Ok(options) => options,
25 Err(message) => {
26 println!("{message}");
27 std::process::exit(1);
28 }
29 }
30}
31
32fn init_repository_or_exit(path_to_repo: &str) -> Repository {
33 match Repository::open(path_to_repo) {
34 Ok(repo) => repo,
35 Err(e) => {
36 println!("Failed to create repository: {e:?}");
37 std::process::exit(1);
38 }
39 }
40}
41
42fn print_diff_summary(diff: &Diff, elapsed_nanos: u128) {
43 diff.added().for_each(|created| {
44 println!("+++ {:?}", created.path);
45 });
46 diff.deleted().for_each(|deleted| {
47 println!("--- {:?}", deleted.path);
48 });
49 diff.modified().for_each(|modified| {
50 println!("mod {:?}", modified.path);
51 });
52
53 println!(
54 "created {} / deleted {} / modified {} / total {}",
55 diff.added().count(),
56 diff.deleted().count(),
57 diff.modified().count(),
58 diff.added().count() + diff.deleted().count() + diff.modified().count()
59 );
60 println!("diff took {elapsed_nanos} nanos ");
61}
62
63struct Options {
64 path_to_repo: String,
65 base_revision: String,
66 head_revision: HeadRevision,
67}
68
69enum HeadRevision {
70 Head,
71 Commit(String),
72}
73
74impl Options {
75 fn parse(args: Args) -> Result<Self, String> {
76 let args: Vec<String> = args.collect();
77 if args.len() != 4 {
78 return Err(format!(
79 "Usage: {} <path-to-repo> <base-revision> <head-revision>\n\
80 \tpath-to-repo: Path to the directory containing .git subdirectory\n\
81 \tbase-revision: Git commit ID of the base revision (one that will be considered less recent)\n\
82 \thead-revision: Git commit ID of the head revision (one that will be considered more recent) or 'HEAD' to use current git HEAD\n",
83 args[0]));
84 }
85
86 let path_to_repo = args[1].clone();
87 let base_revision = args[2].clone();
88 let head_revision = {
89 if args[3].eq_ignore_ascii_case("HEAD") {
90 HeadRevision::Head
91 } else {
92 HeadRevision::Commit(args[3].clone())
93 }
94 };
95
96 Ok(Options {
97 path_to_repo,
98 base_revision,
99 head_revision,
100 })
101 }
102}