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