use crate::cargo::{current_metadata, package};
use anyhow::{anyhow, Context, Result};
use rust_embed::RustEmbed;
use std::{
fs::{create_dir_all, OpenOptions},
io::Write,
path::Path,
};
#[derive(RustEmbed)]
#[folder = "template"]
#[exclude = "Cargo.lock"]
#[exclude = "target/*"]
struct Template;
pub fn new_template(to: &Path) -> Result<()> {
for path in Template::iter() {
let embedded_file = Template::get(&path)
.ok_or_else(|| anyhow!("Could not get embedded file `{}`", path))?;
let to_path = to.join(path.trim_end_matches('~'));
let parent = to_path
.parent()
.ok_or_else(|| anyhow!("Could not get parent directory"))?;
create_dir_all(parent).with_context(|| {
format!("`create_dir_all` failed for `{}`", parent.to_string_lossy())
})?;
let mut file = OpenOptions::new()
.create(true)
.write(true)
.open(&to_path)
.with_context(|| format!("Could not open `{}`", to_path.to_string_lossy()))?;
file.write_all(&embedded_file.data)
.with_context(|| format!("Could not write to {to_path:?}"))?;
}
Ok(())
}
pub fn isolate(path: &Path) -> Result<()> {
let manifest = path.join("Cargo.toml");
let mut file = OpenOptions::new()
.write(true)
.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()
.write(true)
.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 != "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 std::fs::read_to_string;
use toml_edit::{Document, Item};
#[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 paths = Template::iter()
.filter(|path| PATHS.binary_search(&&**path).is_err())
.collect::<Vec<_>>();
assert!(paths.is_empty(), "found {paths:#?}");
}
#[test]
fn template_has_initial_version() {
let contents =
read_to_string(Path::new(env!("CARGO_MANIFEST_DIR")).join("template/Cargo.toml~"))
.unwrap();
let document = contents.parse::<Document>().unwrap();
let version = document
.as_table()
.get("package")
.and_then(Item::as_table)
.and_then(|table| table.get("version"))
.and_then(Item::as_str)
.unwrap();
assert_eq!("0.1.0", version);
}
}