Line data Source code
1 : // smoelius: Since the update to `rust_embed` 8.3.0, `unnecessary_conversion_for_trait` started
2 : // firing on `struct Template`. Requiring `!expr.span.from_expansion()` in
3 : // `unnecessary_conversion_for_trait` causes one of its tests to fail. So allow the lint for now.
4 : // smoelius: `abs_home_path` now fires as well.
5 : #![cfg_attr(dylint_lib = "general", allow(abs_home_path))]
6 : #![cfg_attr(dylint_lib = "overscoped_allow", allow(overscoped_allow))]
7 : #![cfg_attr(dylint_lib = "supplementary", allow(unnecessary_conversion_for_trait))]
8 :
9 : use crate::cargo::{current_metadata, package};
10 : use anyhow::{anyhow, Context, Result};
11 : use rust_embed::RustEmbed;
12 : use std::{
13 : fs::{create_dir_all, OpenOptions},
14 : io::Write,
15 : path::Path,
16 : };
17 :
18 164 : #[derive(RustEmbed)]
19 : #[folder = "template"]
20 : #[exclude = "Cargo.lock"]
21 : #[exclude = "target/*"]
22 : struct Template;
23 :
24 9 : pub fn new_template(to: &Path) -> Result<()> {
25 81 : for path in Template::iter() {
26 72 : let embedded_file = Template::get(&path)
27 72 : .ok_or_else(|| anyhow!("Could not get embedded file `{}`", path))?;
28 72 : let to_path = to.join(path.trim_end_matches('~'));
29 72 : let parent = to_path
30 72 : .parent()
31 72 : .ok_or_else(|| anyhow!("Could not get parent directory"))?;
32 72 : create_dir_all(parent).with_context(|| {
33 0 : format!("`create_dir_all` failed for `{}`", parent.to_string_lossy())
34 72 : })?;
35 72 : let mut file = OpenOptions::new()
36 72 : .create(true)
37 72 : .truncate(true)
38 72 : .write(true)
39 72 : .open(&to_path)
40 72 : .with_context(|| format!("Could not open `{}`", to_path.to_string_lossy()))?;
41 72 : file.write_all(&embedded_file.data)
42 72 : .with_context(|| format!("Could not write to {to_path:?}"))?;
43 : }
44 :
45 9 : Ok(())
46 9 : }
47 :
48 : // smoelius: If a package is checked out in the current directory, this must be dealt with:
49 : // error: current package believes it's in a workspace when it's not
50 13 : pub fn isolate(path: &Path) -> Result<()> {
51 13 : let manifest = path.join("Cargo.toml");
52 13 : let mut file = OpenOptions::new()
53 13 : .append(true)
54 13 : .open(&manifest)
55 13 : .with_context(|| format!("Could not open `{}`", manifest.to_string_lossy()))?;
56 :
57 13 : writeln!(file)
58 13 : .and_then(|()| writeln!(file, "[workspace]"))
59 13 : .with_context(|| format!("Could not write to `{}`", manifest.to_string_lossy()))?;
60 :
61 13 : Ok(())
62 13 : }
63 :
64 : // smoelius: If you clone, say, `dylint-template` and run `cargo test` on it, it will obtain Dylint
65 : // packages from `crates.io`. But for the tests in this repository, you often want it to use the
66 : // packages in this repository. The function `use_local_packages` patches a workspace's `Cargo.toml`
67 : // file to do so.
68 9 : pub fn use_local_packages(path: &Path) -> Result<()> {
69 9 : let metadata = current_metadata()?;
70 :
71 9 : let manifest = path.join("Cargo.toml");
72 :
73 9 : let mut file = OpenOptions::new()
74 9 : .append(true)
75 9 : .open(&manifest)
76 9 : .with_context(|| format!("Could not open `{}`", manifest.to_string_lossy()))?;
77 :
78 : // smoelius: `use_local_packages` broke when `dylint_linting` was removed from the workspace.
79 : // For now, add `dylint_linting` manually.
80 9 : writeln!(file)
81 9 : .and_then(|()| writeln!(file, "[patch.crates-io]"))
82 9 : .and_then(|()| {
83 9 : writeln!(
84 9 : file,
85 9 : r#"dylint_linting = {{ path = "{}" }}"#,
86 9 : metadata
87 9 : .workspace_root
88 9 : .join("utils/linting")
89 9 : .to_string()
90 9 : .replace('\\', "\\\\")
91 9 : )
92 9 : })
93 9 : .with_context(|| format!("Could not write to `{}`", manifest.to_string_lossy()))?;
94 :
95 72 : for package_id in &metadata.workspace_members {
96 63 : let package = package(&metadata, package_id)?;
97 63 : if package.publish == Some(vec![])
98 45 : || package
99 45 : .targets
100 45 : .iter()
101 72 : .all(|target| target.kind.iter().all(|kind| kind != "lib"))
102 : {
103 36 : continue;
104 27 : }
105 27 : let path = package
106 27 : .manifest_path
107 27 : .parent()
108 27 : .ok_or_else(|| anyhow!("Could not get parent directory"))?;
109 27 : writeln!(
110 27 : file,
111 27 : r#"{} = {{ path = "{}" }}"#,
112 27 : package.name,
113 27 : path.to_string().replace('\\', "\\\\")
114 27 : )
115 27 : .with_context(|| format!("Could not write to `{}`", manifest.to_string_lossy()))?;
116 : }
117 :
118 9 : Ok(())
119 9 : }
120 :
121 : #[cfg(test)]
122 : mod test {
123 : use super::*;
124 : use std::fs::read_to_string;
125 : use toml_edit::{DocumentMut, Item};
126 :
127 : #[cfg_attr(
128 : dylint_lib = "assert_eq_arg_misordering",
129 : allow(assert_eq_arg_misordering)
130 : )]
131 : #[test]
132 1 : fn template_includes_only_whitelisted_paths() {
133 : const PATHS: [&str; 8] = [
134 : ".cargo/config.toml",
135 : ".gitignore",
136 : "Cargo.toml~",
137 : "README.md",
138 : "rust-toolchain",
139 : "src/lib.rs",
140 : "ui/main.rs",
141 : "ui/main.stderr",
142 : ];
143 :
144 1 : let mut paths_sorted = PATHS.to_vec();
145 1 : paths_sorted.sort_unstable();
146 1 : assert_eq!(paths_sorted, PATHS);
147 :
148 1 : let paths = Template::iter()
149 8 : .filter(|path| PATHS.binary_search(&&**path).is_err())
150 1 : .collect::<Vec<_>>();
151 1 :
152 1 : assert!(paths.is_empty(), "found {paths:#?}");
153 1 : }
154 :
155 : #[test]
156 1 : fn template_has_initial_version() {
157 1 : let contents = read_to_string("template/Cargo.toml~").unwrap();
158 1 : let document = contents.parse::<DocumentMut>().unwrap();
159 1 : let version = document
160 1 : .as_table()
161 1 : .get("package")
162 1 : .and_then(Item::as_table)
163 1 : .and_then(|table| table.get("version"))
164 1 : .and_then(Item::as_str)
165 1 : .unwrap();
166 1 : assert_eq!("0.1.0", version);
167 1 : }
168 : }
|