1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use std::{borrow::Cow, fmt, fs::File};
use cargo_metadata::camino::{Utf8Path, Utf8PathBuf};
use eyre::{ensure, eyre};
use crate::Result;
#[tracing::instrument(name = "create_file" fields(path = %path.as_ref().to_relative()), err)]
pub fn create_file(path: impl AsRef<Utf8Path>) -> Result<File> {
let path = path.as_ref();
let dir = path.parent().ok_or_else(|| eyre!("path has no parent"))?;
create_dir(dir)?;
tracing::info!("creating file");
let file = File::create(path)?;
Ok(file)
}
#[tracing::instrument(name = "create_dir" fields(path = %path.as_ref().to_relative()), err)]
pub fn create_dir(path: impl AsRef<Utf8Path>) -> Result<()> {
let path = path.as_ref();
if !path.is_dir() {
tracing::info!("creating directory");
std::fs::create_dir_all(path)?;
}
Ok(())
}
#[tracing::instrument(name = "remove_dir" fields(path = %path.as_ref().to_relative()), err)]
pub fn remove_dir(path: impl AsRef<Utf8Path>) -> Result<()> {
let path = path.as_ref();
if path.is_dir() {
tracing::info!("removing directory");
std::fs::remove_dir_all(path)?;
}
Ok(())
}
pub fn create_or_cleanup_dir(dir: impl AsRef<Utf8Path>) -> Result<()> {
let dir = dir.as_ref();
remove_dir(dir)?;
create_dir(dir)?;
Ok(())
}
#[tracing::instrument(name = "copy" skip_all, err)]
pub fn copy(from: impl AsRef<Utf8Path>, to: impl AsRef<Utf8Path>) -> Result<()> {
let from = from.as_ref();
let to = to.as_ref();
if let Some(parent) = to.parent() {
create_dir(parent)?;
}
tracing::info!("{} -> {}", from.to_relative(), to.to_relative());
ensure!(from.is_file(), "not a file: {}", from.to_relative());
std::fs::copy(from, to)?;
Ok(())
}
pub trait ToRelative {
type Output: fmt::Display;
fn to_relative(self) -> Self::Output;
}
impl<'a> ToRelative for &'a Utf8Path {
type Output = Cow<'a, Utf8Path>;
fn to_relative(self) -> Self::Output {
if self.is_relative() {
return Cow::Borrowed(self);
}
let current_dir = std::env::current_dir()
.ok()
.and_then(|path| Utf8PathBuf::try_from(path).ok());
let mut current_dir = current_dir.as_deref();
let mut prefix = Utf8PathBuf::new();
while let Some(cur_dir) = current_dir {
if cur_dir.parent().is_none() {
return Cow::Borrowed(self);
}
if let Ok(relative) = self.strip_prefix(cur_dir) {
if prefix != "" {
return Cow::Owned(prefix.join(relative));
}
return if relative == "" {
Cow::Borrowed(Utf8Path::new("."))
} else {
Cow::Borrowed(relative)
};
}
current_dir = cur_dir.parent();
prefix.push("..");
}
Cow::Borrowed(self)
}
}
impl<'a> ToRelative for &'a Utf8PathBuf {
type Output = Cow<'a, Utf8Path>;
fn to_relative(self) -> Self::Output {
<&Utf8Path>::to_relative(self)
}
}