1use std::fs;
2#[cfg(unix)]
3use std::os::unix::fs::symlink;
4use std::path::{Path, PathBuf};
5use tempfile::TempDir;
6
7pub struct TestWorkspace {
8 root: TempDir,
9}
10
11impl Default for TestWorkspace {
12 fn default() -> Self {
13 Self::new()
14 }
15}
16
17impl TestWorkspace {
18 pub fn new() -> Self {
19 Self { root: TempDir::new().expect("failed to create temp workspace") }
20 }
21
22 pub fn file(self, path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> Self {
23 self.write(path, contents);
24 self
25 }
26
27 pub fn files<P: AsRef<Path>, C: AsRef<[u8]>>(self, files: impl IntoIterator<Item = (P, C)>) -> Self {
28 files.into_iter().fold(self, |workspace, (path, contents)| workspace.file(path, contents))
29 }
30
31 pub fn dir(self, path: impl AsRef<Path>) -> Self {
32 fs::create_dir_all(self.path(path)).expect("failed to create directory");
33 self
34 }
35
36 #[cfg(unix)]
37 pub fn symlink(self, target: impl AsRef<Path>, link: impl AsRef<Path>) -> Self {
38 symlink(self.path(target), self.path(link)).expect("failed to create symlink");
39 self
40 }
41
42 pub fn write(&self, path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> PathBuf {
43 let path = self.path(path);
44 if let Some(parent) = path.parent() {
45 fs::create_dir_all(parent).expect("failed to create parent directory");
46 }
47 fs::write(&path, contents).expect("failed to write file");
48 path
49 }
50
51 pub fn root(&self) -> &Path {
52 self.root.path()
53 }
54
55 pub fn root_string(&self) -> String {
56 self.root().to_string_lossy().into_owned()
57 }
58
59 pub fn path(&self, path: impl AsRef<Path>) -> PathBuf {
60 self.root().join(path)
61 }
62
63 pub fn path_string(&self, path: impl AsRef<Path>) -> String {
64 self.path(path).to_string_lossy().into_owned()
65 }
66
67 pub fn git_init(self) -> Self {
68 git_init(self.root());
69 self
70 }
71
72 pub fn commit(&self, message: &str) -> String {
73 git_commit(self.root(), message)
74 }
75
76 pub fn git(&self, args: &[&str]) -> String {
77 git(self.root(), args)
78 }
79}
80
81pub fn git_init(dir: &Path) {
82 git(dir, &["-c", "init.templateDir=", "init", "--quiet", "--initial-branch=main"]);
83 for (key, value) in [
84 ("user.name", "Test"),
85 ("user.email", "test@example.com"),
86 ("commit.gpgsign", "false"),
87 ("core.hooksPath", "/dev/null"),
88 ] {
89 git(dir, &["config", key, value]);
90 }
91}
92
93pub fn git_commit(dir: &Path, message: &str) -> String {
94 git(dir, &["add", "-A"]);
95 git(dir, &["commit", "--quiet", "-m", message]);
96 git(dir, &["rev-parse", "HEAD"])
97}
98
99pub fn git(dir: &Path, args: &[&str]) -> String {
100 let stdout = crate::git::git(Some(dir), args).unwrap_or_else(|error| panic!("{error}"));
101 String::from_utf8_lossy(&stdout).trim().to_owned()
102}