#![cfg_attr(dylint_lib = "general", allow(abs_home_path))]
#![cfg_attr(dylint_lib = "supplementary", allow(unnecessary_conversion_for_trait))]
use crate::cargo::{current_metadata, package};
use anyhow::{Context, Result, anyhow};
use cargo_metadata::TargetKind;
use std::{fs::OpenOptions, io::Write, path::Path};
use tar::Archive;
const TEMPLATE_TAR: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/template.tar"));
pub fn new_template(to: &Path) -> Result<()> {
Archive::new(TEMPLATE_TAR)
.unpack(to)
.with_context(|| "Could not unpack archive")?;
Ok(())
}
pub fn isolate(path: &Path) -> Result<()> {
let manifest = path.join("Cargo.toml");
let mut file = OpenOptions::new()
.append(true)
.open(&manifest)
.with_context(|| format!("Could not open `{}`", manifest.to_string_lossy()))?;
writeln!(file)
.and_then(|()| writeln!(file, "[workspace]"))
.with_context(|| format!("Could not write to `{}`", manifest.to_string_lossy()))?;
Ok(())
}
pub fn use_local_packages(path: &Path) -> Result<()> {
let metadata = current_metadata()?;
let manifest = path.join("Cargo.toml");
let mut file = OpenOptions::new()
.append(true)
.open(&manifest)
.with_context(|| format!("Could not open `{}`", manifest.to_string_lossy()))?;
writeln!(file)
.and_then(|()| writeln!(file, "[patch.crates-io]"))
.and_then(|()| {
writeln!(
file,
r#"dylint_linting = {{ path = "{}" }}"#,
metadata
.workspace_root
.join("utils/linting")
.to_string()
.replace('\\', "\\\\")
)
})
.with_context(|| format!("Could not write to `{}`", manifest.to_string_lossy()))?;
for package_id in &metadata.workspace_members {
let package = package(&metadata, package_id)?;
if package.publish == Some(vec![])
|| package
.targets
.iter()
.all(|target| target.kind.iter().all(|kind| kind != &TargetKind::Lib))
{
continue;
}
let path = package
.manifest_path
.parent()
.ok_or_else(|| anyhow!("Could not get parent directory"))?;
writeln!(
file,
r#"{} = {{ path = "{}" }}"#,
package.name,
path.to_string().replace('\\', "\\\\")
)
.with_context(|| format!("Could not write to `{}`", manifest.to_string_lossy()))?;
}
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
use crate::env;
use std::fs::{copy, read, read_to_string};
use tempfile::TempDir;
#[cfg_attr(
dylint_lib = "assert_eq_arg_misordering",
allow(assert_eq_arg_misordering)
)]
#[test]
fn template_includes_only_whitelisted_paths() {
const PATHS: [&str; 8] = [
".cargo/config.toml",
".gitignore",
"Cargo.toml",
"README.md",
"rust-toolchain",
"src/lib.rs",
"ui/main.rs",
"ui/main.stderr",
];
let mut paths_sorted = PATHS.to_vec();
paths_sorted.sort_unstable();
assert_eq!(paths_sorted, PATHS);
let mut archive = Archive::new(TEMPLATE_TAR);
let entries = archive.entries().unwrap();
let mut paths = Vec::new();
for result in entries {
let entry = result.unwrap();
let path = entry.path().unwrap();
let path_str = path.to_str().map(ToOwned::to_owned).unwrap();
if PATHS.binary_search(&path_str.as_str()).is_err() {
paths.push(path_str);
}
}
assert!(paths.is_empty(), "found {paths:#?}");
}
#[test]
fn template_has_initial_version() {
let contents = read_to_string("template/Cargo.toml").unwrap();
let document = contents.parse::<toml::Table>().unwrap();
let version = document
.get("package")
.and_then(toml::Value::as_table)
.and_then(|table| table.get("version"))
.and_then(toml::Value::as_str)
.unwrap();
assert_eq!("0.1.0", version);
}
#[cfg_attr(dylint_lib = "general", allow(non_thread_safe_call_in_test))]
#[test]
fn check_template_tar() {
let dir = build_template_tar();
let path_buf = dir.path().join("template.tar");
if env::enabled("BLESS") {
copy(
path_buf,
concat!(env!("CARGO_MANIFEST_DIR"), "/template.tar"),
)
.unwrap();
} else {
let contents = read(dir.path().join("template.tar")).unwrap();
assert_eq!(TEMPLATE_TAR, contents);
}
}
fn build_template_tar() -> TempDir {
use std::{fs::File, path::Path};
use tar::{Builder, HeaderMode};
use walkdir::WalkDir;
let tempdir = TempDir::new().unwrap();
let path_buf = tempdir.path().join("template.tar");
let file = File::create(path_buf).unwrap();
let mut archive = Builder::new(file);
archive.mode(HeaderMode::Deterministic);
let root = Path::new("template");
for result in WalkDir::new(root)
.sort_by_file_name()
.into_iter()
.filter_entry(|entry| {
let filename = entry.file_name();
filename != "Cargo.lock" && filename != "target"
})
{
let entry = result.unwrap();
let path = entry.path();
if !path.is_file() {
continue;
}
let mut file = File::open(path).unwrap();
let path_stripped = path.strip_prefix(root).unwrap();
archive.append_file(path_stripped, &mut file).unwrap();
}
tempdir
}
}