]> git.lizzy.rs Git - rust.git/blobdiff - clippy_dev/src/lintcheck.rs
implement the download_and_extract() step for git sources
[rust.git] / clippy_dev / src / lintcheck.rs
index e2099ff98c836ccd0093b83fac2e9fa68ee571c3..63f78db13f8d9748f9c0cad17758afcf1c20d58b 100644 (file)
 use clap::ArgMatches;
 use serde::{Deserialize, Serialize};
 use serde_json::Value;
+//use git2::Repository;
 
 // use this to store the crates when interacting with the crates.toml file
 #[derive(Debug, Serialize, Deserialize)]
 struct CrateList {
-    crates: HashMap<String, Vec<String>>,
+    crates: HashMap<String, TomlCrate>,
 }
 
 // crate data we stored in the toml, can have multiple versions per crate
 // A single TomlCrate is laster mapped to several CrateSources in that case
+#[derive(Debug, Serialize, Deserialize)]
 struct TomlCrate {
     name: String,
-    versions: Vec<String>,
+    versions: Option<Vec<String>>,
+    git_url: Option<String>,
+    git_hash: Option<String>,
 }
 
 // represents an archive we download from crates.io
 #[derive(Debug, Serialize, Deserialize, Eq, Hash, PartialEq)]
-struct CrateSource {
-    name: String,
-    version: String,
+enum CrateSource {
+    CratesIo { name: String, version: String },
+    Git { name: String, url: String, commit: String },
 }
 
 // represents the extracted sourcecode of a crate
+// we actually don't need to special-case git repos here because it does not matter for clippy, yay! (clippy only needs a simple path)
 #[derive(Debug)]
 struct Crate {
     version: String,
@@ -61,7 +66,7 @@ impl std::fmt::Display for ClippyWarning {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         writeln!(
             f,
-            r#"{}/{}/{}:{}:{} {} "{}""#,
+            r#"{}-{}/{}:{}:{} {} "{}""#,
             &self.crate_name, &self.crate_version, &self.file, &self.line, &self.column, &self.linttype, &self.message
         )
     }
@@ -69,40 +74,70 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 
 impl CrateSource {
     fn download_and_extract(&self) -> Crate {
-        let extract_dir = PathBuf::from("target/lintcheck/crates");
-        let krate_download_dir = PathBuf::from("target/lintcheck/downloads");
-
-        // url to download the crate from crates.io
-        let url = format!(
-            "https://crates.io/api/v1/crates/{}/{}/download",
-            self.name, self.version
-        );
-        println!("Downloading and extracting {} {} from {}", self.name, self.version, url);
-        let _ = std::fs::create_dir("target/lintcheck/");
-        let _ = std::fs::create_dir(&krate_download_dir);
-        let _ = std::fs::create_dir(&extract_dir);
-
-        let krate_file_path = krate_download_dir.join(format!("{}-{}.crate.tar.gz", &self.name, &self.version));
-        // don't download/extract if we already have done so
-        if !krate_file_path.is_file() {
-            // create a file path to download and write the crate data into
-            let mut krate_dest = std::fs::File::create(&krate_file_path).unwrap();
-            let mut krate_req = ureq::get(&url).call().unwrap().into_reader();
-            // copy the crate into the file
-            std::io::copy(&mut krate_req, &mut krate_dest).unwrap();
-
-            // unzip the tarball
-            let ungz_tar = flate2::read::GzDecoder::new(std::fs::File::open(&krate_file_path).unwrap());
-            // extract the tar archive
-            let mut archive = tar::Archive::new(ungz_tar);
-            archive.unpack(&extract_dir).expect("Failed to extract!");
-        }
-        // crate is extracted, return a new Krate object which contains the path to the extracted
-        // sources that clippy can check
-        Crate {
-            version: self.version.clone(),
-            name: self.name.clone(),
-            path: extract_dir.join(format!("{}-{}/", self.name, self.version)),
+        match self {
+            CrateSource::CratesIo { name, version } => {
+                let extract_dir = PathBuf::from("target/lintcheck/crates");
+                let krate_download_dir = PathBuf::from("target/lintcheck/downloads");
+
+                // url to download the crate from crates.io
+                let url = format!("https://crates.io/api/v1/crates/{}/{}/download", name, version);
+                println!("Downloading and extracting {} {} from {}", name, version, url);
+                let _ = std::fs::create_dir("target/lintcheck/");
+                let _ = std::fs::create_dir(&krate_download_dir);
+                let _ = std::fs::create_dir(&extract_dir);
+
+                let krate_file_path = krate_download_dir.join(format!("{}-{}.crate.tar.gz", name, version));
+                // don't download/extract if we already have done so
+                if !krate_file_path.is_file() {
+                    // create a file path to download and write the crate data into
+                    let mut krate_dest = std::fs::File::create(&krate_file_path).unwrap();
+                    let mut krate_req = ureq::get(&url).call().unwrap().into_reader();
+                    // copy the crate into the file
+                    std::io::copy(&mut krate_req, &mut krate_dest).unwrap();
+
+                    // unzip the tarball
+                    let ungz_tar = flate2::read::GzDecoder::new(std::fs::File::open(&krate_file_path).unwrap());
+                    // extract the tar archive
+                    let mut archive = tar::Archive::new(ungz_tar);
+                    archive.unpack(&extract_dir).expect("Failed to extract!");
+                }
+                // crate is extracted, return a new Krate object which contains the path to the extracted
+                // sources that clippy can check
+                Crate {
+                    version: version.clone(),
+                    name: name.clone(),
+                    path: extract_dir.join(format!("{}-{}/", name, version)),
+                }
+            },
+            CrateSource::Git { name, url, commit } => {
+                let repo_path = {
+                    let mut repo_path = PathBuf::from("target/lintcheck/downloads");
+                    // add a -git suffix in case we have the same crate from crates.io and a git repo
+                    repo_path.push(format!("{}-git", name));
+                    repo_path
+                };
+                // clone the repo if we have not done so
+                if !repo_path.is_dir() {
+                    Command::new("git")
+                        .arg("clone")
+                        .arg(url)
+                        .arg(&repo_path)
+                        .output()
+                        .expect("Failed to clone git repo!");
+                }
+                // check out the commit/branch/whatever
+                Command::new("git")
+                    .arg("checkout")
+                    .arg(commit)
+                    .output()
+                    .expect("Failed to check out commit");
+
+                Crate {
+                    version: commit.clone(),
+                    name: name.clone(),
+                    path: repo_path,
+                }
+            },
         }
     }
 }
@@ -114,7 +149,7 @@ fn run_clippy_lints(&self, cargo_clippy_path: &PathBuf) -> Vec<ClippyWarning> {
 
         let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir/");
 
-        let all_output = std::process::Command::new(cargo_clippy_path)
+        let all_output = std::process::Command::new(&cargo_clippy_path)
             .env("CARGO_TARGET_DIR", shared_target_dir)
             // lint warnings will look like this:
             // src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter`
@@ -128,7 +163,12 @@ fn run_clippy_lints(&self, cargo_clippy_path: &PathBuf) -> Vec<ClippyWarning> {
             ])
             .current_dir(&self.path)
             .output()
-            .unwrap();
+            .unwrap_or_else(|error| {
+                dbg!(error);
+                dbg!(&cargo_clippy_path);
+                dbg!(&self.path);
+                panic!("something was not found?")
+            });
         let stdout = String::from_utf8_lossy(&all_output.stdout);
         let output_lines = stdout.lines();
         //dbg!(&output_lines);
@@ -160,19 +200,37 @@ fn read_crates() -> Vec<CrateSource> {
     let tomlcrates: Vec<TomlCrate> = crate_list
         .crates
         .into_iter()
-        .map(|(name, versions)| TomlCrate { name, versions })
+        .map(|(_cratename, tomlcrate)| tomlcrate)
         .collect();
 
     // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate =>
     // multiple Cratesources)
     let mut crate_sources = Vec::new();
     tomlcrates.into_iter().for_each(|tk| {
-        tk.versions.iter().for_each(|ver| {
-            crate_sources.push(CrateSource {
+        // if we have multiple versions, save each one
+        if let Some(ref versions) = tk.versions {
+            versions.iter().for_each(|ver| {
+                crate_sources.push(CrateSource::CratesIo {
+                    name: tk.name.clone(),
+                    version: ver.to_string(),
+                });
+            })
+        }
+        // otherwise, we should have a git source
+        if tk.git_url.is_some() && tk.git_hash.is_some() {
+            crate_sources.push(CrateSource::Git {
                 name: tk.name.clone(),
-                version: ver.to_string(),
+                url: tk.git_url.clone().unwrap(),
+                commit: tk.git_hash.clone().unwrap(),
             });
-        })
+        }
+        // if we have a version as well as a git data OR only one git data, something is funky
+        if tk.versions.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some())
+            || tk.git_hash.is_some() != tk.git_url.is_some()
+        {
+            dbg!(tk);
+            unreachable!("Failed to translate TomlCrate into CrateSource!");
+        }
     });
     crate_sources
 }
@@ -229,13 +287,13 @@ pub fn run(clap_config: &ArgMatches) {
 
     let clippy_warnings: Vec<ClippyWarning> = if let Some(only_one_crate) = clap_config.value_of("only") {
         // if we don't have the specified crated in the .toml, throw an error
-        if !crates.iter().any(|krate| krate.name == only_one_crate) {
+        /*   if !crates.iter().any(|krate| krate.name == only_one_crate) {
             eprintln!(
                 "ERROR: could not find crate '{}' in clippy_dev/lintcheck_crates.toml",
                 only_one_crate
             );
             std::process::exit(1);
-        }
+        } */ //@FIXME
 
         // only check a single crate that was passed via cmdline
         crates