From 7dc0b9178293db389245f01fe8ecd2588a56fc3d Mon Sep 17 00:00:00 2001 From: Daniil Demidko Date: Thu, 4 Apr 2024 13:48:46 +0600 Subject: [PATCH] MVP is ready --- Cargo.lock | 45 ++++++++++ Cargo.toml | 3 +- README.md | 2 +- src/arch_hunk.rs | 5 -- src/git.rs | 67 --------------- src/languages/arch_diff.rs | 7 -- src/languages/java_arch_diff.rs | 50 ------------ src/languages/mod.rs | 3 - src/main.rs | 140 +++++++++++++++++++++++++++++++- 9 files changed, 184 insertions(+), 138 deletions(-) delete mode 100644 src/arch_hunk.rs delete mode 100644 src/git.rs delete mode 100644 src/languages/arch_diff.rs delete mode 100644 src/languages/java_arch_diff.rs delete mode 100644 src/languages/mod.rs diff --git a/Cargo.lock b/Cargo.lock index efa4268..7cfebd0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,11 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "archdiff" version = "2024.3.12" dependencies = [ "git2", + "regex", "similar", ] @@ -121,6 +131,12 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + [[package]] name = "openssl-probe" version = "0.1.5" @@ -151,6 +167,35 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + [[package]] name = "similar" version = "2.5.0" diff --git a/Cargo.toml b/Cargo.toml index b0cbf70..030aa57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,5 @@ panic = "abort" [dependencies] git2 = "0.18.3" -similar = "2.5.0" \ No newline at end of file +regex = "1.10.4" +similar = "2.5.0" diff --git a/README.md b/README.md index 0ba5387..9689c29 100644 --- a/README.md +++ b/README.md @@ -16,5 +16,5 @@ cargo install --git https://github.com/demidko/archdiff ## Usage ```shell -archdiff old_branch new_branch +archdiff [old_branch] [new_branch] ``` diff --git a/src/arch_hunk.rs b/src/arch_hunk.rs deleted file mode 100644 index 8f12d48..0000000 --- a/src/arch_hunk.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub struct ArchHunk { - pub filename: String, - pub old_arch: String, - pub new_arch: String, -} \ No newline at end of file diff --git a/src/git.rs b/src/git.rs deleted file mode 100644 index 0e2164b..0000000 --- a/src/git.rs +++ /dev/null @@ -1,67 +0,0 @@ -use std::collections::hash_set::Difference; -use std::fmt::Debug; -use std::path::Path; -use std::str; - -use git2::{Diff, DiffDelta, DiffFindOptions, DiffHunk, DiffLine, Object, ObjectType, Repository}; - -pub fn open_current_repo() -> Repository { - match Repository::open(".") { - Ok(repo) => repo, - Err(e) => panic!("Failed to open repo: {}", e.message()) - } -} - -pub fn diff_branches<'a>(repo: &'a Repository, old_branch: &str, new_branch: &str) -> Diff<'a> { - let old_obj = make_tree_object(repo, old_branch); - let old_tree = old_obj.as_tree(); - let new_obj = make_tree_object(repo, new_branch); - let new_tree = new_obj.as_tree(); - let diff = repo.diff_tree_to_tree(old_tree, new_tree, None); - let mut opts = DiffFindOptions::new(); - opts.renames(true); - opts.copies(true); - let mut diff = diff.unwrap(); - let diff_ref = Some(&mut opts); - diff.find_similar(diff_ref).unwrap(); - diff -} - -pub fn print_diff_line(delta: DiffDelta, hunk: Option, line: DiffLine) -> bool { - println!(); - println!("{:?} {:?}", delta.old_file().path(), delta.new_file().path()); - println!("{:?}", delta.status()); - println!("{:?} {:?}", line.old_lineno(), line.new_lineno()); - println!("{:?}", line.origin_value()); - - //println!("{} {} {} {}", hunk.old_start(), hunk.old_lines(), hunk.new_start(), hunk.new_lines()); - print!("{}", str::from_utf8(line.content()).unwrap()); - true -} - -fn extract_path<'a>(diff_delta: &'a DiffDelta) -> &'a Path { - let new_file = diff_delta.new_file().path(); - let old_file = diff_delta.old_file().path(); - new_file.or(old_file).unwrap() -} - -fn make_tree_object<'a>(repo: &'a Repository, arg: &str) -> Object<'a> { - let obj = repo.revparse_single(arg).unwrap(); - let tree_object = obj.peel(ObjectType::Tree); - tree_object.unwrap() -} - -#[cfg(test)] -mod tests { - use git2::DiffFormat; - - use crate::git::{diff_branches, open_current_repo, print_diff_line}; - - #[test] - fn it_works() { - let repo = open_current_repo(); - let diff = diff_branches(&repo, "test_branch", "main"); - let diff_format = DiffFormat::Patch; - diff.print(DiffFormat::Patch, print_diff_line).unwrap(); - } -} \ No newline at end of file diff --git a/src/languages/arch_diff.rs b/src/languages/arch_diff.rs deleted file mode 100644 index 592a276..0000000 --- a/src/languages/arch_diff.rs +++ /dev/null @@ -1,7 +0,0 @@ -use std::fmt::Display; - -use git2::DiffLine; - -pub trait ArchDiff: Display { - fn arch_diff(&mut self, filename: &str, diff: &DiffLine); -} \ No newline at end of file diff --git a/src/languages/java_arch_diff.rs b/src/languages/java_arch_diff.rs deleted file mode 100644 index 2e7c8a3..0000000 --- a/src/languages/java_arch_diff.rs +++ /dev/null @@ -1,50 +0,0 @@ -use std::fmt::Display; -use std::ops::Add; - -use diffy::create_patch; -use git2::DiffLine; - -use crate::languages::arch_diff::ArchDiff; - -struct JavaArchDiff { - filename: String, - old_arch: String, - new_arch: String, - diff: String, -} - -impl JavaArchDiff { - fn flush(&mut self) { - let old = &self.old_arch; - let new = &self.new_arch; - let diff = create_patch(old, new); - let diff = diff.to_string(); - self.diff += &diff - } -} - -impl Display for JavaArchDiff { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.diff) - } -} - -impl ArchDiff for JavaArchDiff { - fn arch_diff(&mut self, filename: &str, diff: &DiffLine) { - todo!() - } -} - -#[cfg(test)] -mod tests { - use diffy::{create_patch, DiffOptions}; - - #[test] - fn it_works() { - let old = "ool\nkek\njey trek\n"; - let new = "lol\nkek\njey trep"; - let diff = create_patch(old, new); - println!("{}", diff); - } -} - diff --git a/src/languages/mod.rs b/src/languages/mod.rs deleted file mode 100644 index 1b9e0bc..0000000 --- a/src/languages/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod arch_diff; - -mod java_arch_diff; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index a38630d..6dc7785 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,139 @@ -mod git; -mod arch_hunk; +use std::env::args; +use std::fmt::Debug; +use std::ops::Not; +use std::str; -mod languages; +use DiffFormat::Patch; +use DiffLineType::{AddEOFNL, Addition, Binary, Context, ContextEOFNL, DeleteEOFNL, Deletion, FileHeader, HunkHeader}; +use git2::{Diff, DiffDelta, DiffFindOptions, DiffFormat, DiffHunk, DiffLine, DiffLineType, Object, ObjectType, Repository}; +use similar::DiffableStr; +fn main() { + let help = "Usage: archdiff [OLD_BRANCH] [NEW_BRANCH]"; + let old_branch = args().nth(1).expect(help); + let new_branch = args().nth(2).expect(help); + let repo = open_current_repo(); + diff_branches(&repo, &old_branch, &new_branch) + .print(Patch, print_diff_line) + .unwrap(); +} -fn main() {} \ No newline at end of file +pub fn open_current_repo() -> Repository { + match Repository::open(".") { + Ok(repo) => repo, + Err(e) => panic!("Failed to open repo: {}", e.message()) + } +} + +pub fn diff_branches<'a>(repo: &'a Repository, old_branch: &str, new_branch: &str) -> Diff<'a> { + let old_obj = make_tree_object(repo, old_branch); + let old_tree = old_obj.as_tree(); + let new_obj = make_tree_object(repo, new_branch); + let new_tree = new_obj.as_tree(); + let diff = repo.diff_tree_to_tree(old_tree, new_tree, None); + let mut opts = DiffFindOptions::new(); + opts.renames(true); + opts.copies(true); + let mut diff = diff.unwrap(); + let diff_ref = Some(&mut opts); + diff.find_similar(diff_ref).unwrap(); + diff +} + +pub fn print_diff_line(delta: DiffDelta, _hunk: Option, line: DiffLine) -> bool { + let diff_type = line.origin_value(); + + if is_unsupported_file(&delta) { + return true; + } + if is_unsupported_diff(diff_type) { + return true; + } + + let line = line.content(); + let line = str::from_utf8(line).unwrap(); + + if diff_type == FileHeader { + print!("{}", line); + return true; + } + if diff_type == HunkHeader { + print!("{}", trim_hunk_header(line)); + return true; + } + if is_java_api(line) { + println!("{}{}", prefix(diff_type), trim_java_api(line)); + } + true +} + +fn prefix(diff: DiffLineType) -> char { + match diff { + Context => ' ', + Addition => '+', + Deletion => '-', + _ => panic!("Unexpected type here: {:?}", diff) + } +} + +fn trim_hunk_header(line: &str) -> &str { + if line.starts_with("@@") { + let line = line.trim_start_matches("@@"); + let idx = line.find("@@").unwrap() + 2; + return &line[idx..]; + } + line +} + +fn is_java_api(line: &str) -> bool { + let line = line.trim_start(); + if line.starts_with("public") { + return true; + } + if line.starts_with("class") { + return true; + } + false +} + +fn trim_java_api(line: &str) -> &str { + line.trim_end_matches(" {\n") +} + +fn is_unsupported_file(delta: &DiffDelta) -> bool { + let new_file = delta.new_file().path(); + let old_file = delta.old_file().path(); + let extension = + new_file.or(old_file).unwrap() + .extension().unwrap() + .to_str().unwrap(); + extension != "java" +} + +fn is_unsupported_diff(diff: DiffLineType) -> bool { + match diff { + ContextEOFNL => true, + AddEOFNL => true, + DeleteEOFNL => true, + Binary => true, + _ => false + } +} + +fn make_tree_object<'a>(repo: &'a Repository, arg: &str) -> Object<'a> { + let obj = repo.revparse_single(arg).unwrap(); + let tree_object = obj.peel(ObjectType::Tree); + tree_object.unwrap() +} + +#[cfg(test)] +mod tests { + use crate::trim_hunk_header; + + #[test] + fn trim_hunk_test() { + let header = "@@ -4,7 +4,7 @@ class Test {"; + let header = trim_hunk_header(&header); + assert_eq!(header, " class Test {") + } +} \ No newline at end of file