From 63d50dd1d71b04bac0053f2482b8a2014cba8526 Mon Sep 17 00:00:00 2001
From: benluiwj <wenjie.lui@gmail.com>
Date: Thu, 6 Jun 2024 20:42:13 +0800
Subject: [PATCH] Add `cd` and `git clone` functionality to check-diff crate

Included unit tests for the new functionality as well.
---
 check_diff/Cargo.lock             | 290 ++++++++++++++++++++++++++++++
 check_diff/Cargo.toml             |   4 +
 check_diff/src/lib.rs             |  58 ++++++
 check_diff/src/main.rs            |   8 +-
 check_diff/tests/bash_commands.rs |  12 ++
 check_diff/tests/git.rs           |  16 ++
 6 files changed, 382 insertions(+), 6 deletions(-)
 create mode 100644 check_diff/src/lib.rs
 create mode 100644 check_diff/tests/bash_commands.rs
 create mode 100644 check_diff/tests/git.rs

diff --git a/check_diff/Cargo.lock b/check_diff/Cargo.lock
index 6716ccdf9a0..2abf5af2f98 100644
--- a/check_diff/Cargo.lock
+++ b/check_diff/Cargo.lock
@@ -2,6 +2,15 @@
 # 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 = "anstream"
 version = "0.6.14"
@@ -51,11 +60,26 @@ dependencies = [
  "windows-sys",
 ]
 
+[[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
 [[package]]
 name = "check_diff"
 version = "0.1.0"
 dependencies = [
  "clap",
+ "tempfile",
+ "tracing",
+ "tracing-subscriber",
 ]
 
 [[package]]
@@ -104,6 +128,22 @@ version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
 
+[[package]]
+name = "errno"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
+
 [[package]]
 name = "heck"
 version = "0.5.0"
@@ -116,6 +156,73 @@ version = "1.70.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
 
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.155"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "matchers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+dependencies = [
+ "regex-automata 0.1.10",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+
 [[package]]
 name = "proc-macro2"
 version = "1.0.83"
@@ -134,6 +241,78 @@ dependencies = [
  "proc-macro2",
 ]
 
+[[package]]
+name = "regex"
+version = "1.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata 0.4.7",
+ "regex-syntax 0.8.4",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "regex-syntax 0.6.29",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax 0.8.4",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+
+[[package]]
+name = "rustix"
+version = "0.38.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
 [[package]]
 name = "strsim"
 version = "0.11.1"
@@ -151,6 +330,89 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "tempfile"
+version = "3.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
 [[package]]
 name = "unicode-ident"
 version = "1.0.12"
@@ -163,6 +425,34 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
 
+[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
 [[package]]
 name = "windows-sys"
 version = "0.52.0"
diff --git a/check_diff/Cargo.toml b/check_diff/Cargo.toml
index a1ed154481a..4ae8a5f1f3a 100644
--- a/check_diff/Cargo.toml
+++ b/check_diff/Cargo.toml
@@ -7,3 +7,7 @@ edition = "2021"
 
 [dependencies]
 clap = { version = "4.4.2", features = ["derive"] }
+tracing = "0.1.37"
+tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
+[dev-dependencies]
+tempfile = "3"
diff --git a/check_diff/src/lib.rs b/check_diff/src/lib.rs
new file mode 100644
index 00000000000..b83d67c8b6e
--- /dev/null
+++ b/check_diff/src/lib.rs
@@ -0,0 +1,58 @@
+use std::env;
+use std::io;
+use std::path::Path;
+use std::process::Command;
+use tracing::info;
+
+pub enum GitError {
+    FailedClone { stdout: Vec<u8>, stderr: Vec<u8> },
+    IO(std::io::Error),
+}
+
+impl From<io::Error> for GitError {
+    fn from(error: io::Error) -> Self {
+        GitError::IO(error)
+    }
+}
+
+/// Clone a git repository
+///
+/// Parameters:
+/// url: git clone url
+/// dest: directory where the repo should be cloned
+pub fn clone_git_repo(url: &str, dest: &Path) -> Result<(), GitError> {
+    let git_cmd = Command::new("git")
+        .env("GIT_TERMINAL_PROMPT", "0")
+        .args([
+            "clone",
+            "--quiet",
+            url,
+            "--depth",
+            "1",
+            dest.to_str().unwrap(),
+        ])
+        .output()?;
+
+    // if the git command does not return successfully,
+    // any command on the repo will fail. So fail fast.
+    if !git_cmd.status.success() {
+        let error = GitError::FailedClone {
+            stdout: git_cmd.stdout,
+            stderr: git_cmd.stderr,
+        };
+        return Err(error);
+    }
+
+    info!("Successfully clone repository.");
+    return Ok(());
+}
+
+pub fn change_directory_to_path(dest: &Path) -> io::Result<()> {
+    let dest_path = Path::new(&dest);
+    env::set_current_dir(&dest_path)?;
+    info!(
+        "Current directory: {}",
+        env::current_dir().unwrap().display()
+    );
+    return Ok(());
+}
diff --git a/check_diff/src/main.rs b/check_diff/src/main.rs
index 6d07c1b0df6..01c5926c490 100644
--- a/check_diff/src/main.rs
+++ b/check_diff/src/main.rs
@@ -1,4 +1,5 @@
 use clap::Parser;
+
 /// Inputs for the check_diff script
 #[derive(Parser)]
 struct CliInputs {
@@ -16,10 +17,5 @@ struct CliInputs {
 }
 
 fn main() {
-    let args = CliInputs::parse();
-    println!(
-        "remote_repo_url: {:?}, feature_branch: {:?},
-        optional_commit_hash: {:?}, optional_rustfmt_config: {:?}",
-        args.remote_repo_url, args.feature_branch, args.commit_hash, args.rustfmt_config
-    );
+    let _args = CliInputs::parse();
 }
diff --git a/check_diff/tests/bash_commands.rs b/check_diff/tests/bash_commands.rs
new file mode 100644
index 00000000000..38ee34ef503
--- /dev/null
+++ b/check_diff/tests/bash_commands.rs
@@ -0,0 +1,12 @@
+use check_diff::change_directory_to_path;
+use std::env;
+use tempfile::Builder;
+
+#[test]
+fn cd_test() {
+    // Creates an empty directory in the current working directory
+    let dir = Builder::new().tempdir_in("").unwrap();
+    let dest_path = dir.path();
+    change_directory_to_path(dest_path).unwrap();
+    assert_eq!(env::current_dir().unwrap(), dest_path);
+}
diff --git a/check_diff/tests/git.rs b/check_diff/tests/git.rs
new file mode 100644
index 00000000000..677c3840e1e
--- /dev/null
+++ b/check_diff/tests/git.rs
@@ -0,0 +1,16 @@
+use check_diff::clone_git_repo;
+
+use tempfile::Builder;
+
+#[test]
+fn clone_repo_test() {
+    // Creates an empty directory in the current working directory
+    let dir = Builder::new().tempdir_in("").unwrap();
+    let sample_repo = "https://github.com/rust-lang/rustfmt.git";
+    let dest_path = dir.path();
+    let result = clone_git_repo(sample_repo, dest_path);
+    assert!(result.is_ok());
+    // check whether a .git folder exists after cloning the repo
+    let git_repo = dest_path.join(".git");
+    assert!(git_repo.exists());
+}