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