1#[macro_export]
18macro_rules! create_recursive {
19 ($path:expr) => {
20 let create_recursive_dir = |p: &std::path::Path| {
21 if !p.exists() || !p.is_dir() {
22 let mut builder = std::fs::DirBuilder::new();
23 builder.recursive(true);
24 builder.create(p).expect("Recursive mode won't panic");
25 }
26 };
27
28 create_recursive_dir($path)
29 };
30}
31
32#[macro_export]
68macro_rules! map_miette {
69 ($expr:expr, $wrap_msg:expr, $usage:expr, help = $add_help:expr) => {
71 $expr.map_err(|e| {
72 use crossterm::style::Stylize;
73 miette::miette!(
74 help = format!("{}\nFor more information, try `sericom --help`.", $add_help),
75 "{e}"
76 )
77 .wrap_err(format!("{}\n\n{}\n", $wrap_msg, $usage).red())
78 })
79 };
80
81 ($expr:expr, $wrap_msg:expr, $usage:expr) => {
83 $expr.map_err(|e| {
84 use crossterm::style::Stylize;
85 miette::miette!(help = "For more information, try `sericom --help`.", "{e}")
86 .wrap_err(format!("{}\n\n{}\n", $wrap_msg, $usage).red())
87 })
88 };
89
90 ($expr:expr, $wrap_msg:expr, help = $add_help:expr) => {
92 $expr.map_err(|e| {
93 use crossterm::style::Stylize;
94 miette::miette!(
95 help = format!("{}\nFor more information, try `sericom --help`.", $add_help),
96 "{e}"
97 )
98 .wrap_err(format!("{}", $wrap_msg).red())
99 })
100 };
101
102 ($expr:expr, $wrap_msg:expr) => {
104 $expr.map_err(|e| {
105 use crossterm::style::Stylize;
106 miette::miette!(help = "For more information, try `sericom --help`.", "{e}")
107 .wrap_err(format!("{}", $wrap_msg).red())
108 })
109 };
110}
111
112#[macro_export]
165macro_rules! compat_port_path {
166 ($port:expr, prefix = $prefix:literal) => {{
167 use chrono;
168 use std::path::PathBuf;
169
170 let path_port = $crate::path_utils::get_compat_port_path($port)?;
171 PathBuf::from(format!(
172 "./{}-{}-{}.txt",
173 $prefix,
174 path_port.display(),
175 chrono::Utc::now().format("%m%d%H%M"),
176 ))
177 }};
178
179 ($out_dir:expr, $port:expr, prefix = $prefix:expr) => {{
180 use chrono;
181
182 let path_port = $crate::path_utils::get_compat_port_path($port)?;
183 $out_dir.join(format!(
184 "./{}-{}-{}.txt",
185 $prefix,
186 path_port.display(),
187 chrono::Utc::now().format("%m%d%H%M"),
188 ))
189 }};
190
191 ($out_dir:expr, $port:expr) => {{
192 use chrono;
193
194 let path_port = $crate::path_utils::get_compat_port_path($port)?;
195 $out_dir.join(format!(
196 "./{}-{}.txt",
197 path_port.display(),
198 chrono::Utc::now().format("%m%d%H%M"),
199 ))
200 }};
201
202 ($port:expr) => {{
203 use chrono;
204
205 let path_port = $crate::path_utils::get_compat_port_path($port)?;
206 PathBuf::from(format!(
207 "./{}-{}.txt",
208 path_port.display(),
209 chrono::Utc::now().format("%m%d%H%M"),
210 ))
211 }};
212}
213
214#[doc(hidden)]
215pub fn get_compat_port_path<S>(port: S) -> miette::Result<std::path::PathBuf>
216where
217 S: Into<std::path::PathBuf>,
218{
219 #[cfg(windows)]
220 {
221 Ok(port.into())
222 }
223 #[cfg(unix)]
224 {
225 use miette::{self, WrapErr};
226 use std::path::PathBuf;
227
228 let p: PathBuf = port.into();
229 Ok(PathBuf::from(p.file_name().ok_or(std::io::ErrorKind::InvalidFilename)
230 .map_err(|e| miette::miette!(
231 help = format!("The name of the tracing file is tied to the port being opened, make sure you are using a valid port."),
232 "{e}: '{}'\n",
233 p.display()
234 )).wrap_err_with(|| format!("Could not create file: '{}' for tracing output.\n", p.display()))?))
235 }
236}
237
238macro_rules! push_n_check {
245 ($home:expr, $push:literal) => {
246 $home.push($push);
247 if !$home.exists() {
248 return None;
249 }
250 };
251}
252
253macro_rules! expand_path {
262 ($self:ident, $expand:literal, to = $expand_to:literal) => {{
263 use std::{env, path::PathBuf};
264
265 if $self.starts_with($expand) {
266 let mut home = env::home_dir()?;
267 let expanded: PathBuf = $self.components().skip(1).collect();
268 push_n_check!(home, $expand_to);
269 $self = home.join(expanded);
270 }
271 }};
272
273 ($self:ident, $expand:literal) => {{
274 use std::{env, path::PathBuf};
275
276 if $self.starts_with($expand) {
277 let home = env::home_dir()?;
278 let expanded: PathBuf = $self.components().skip(1).collect();
279 $self = home.join(expanded);
280 }
281 }};
282}
283
284#[cfg(windows)]
286macro_rules! expand_env_path {
287 ($self:ident, $expand:literal, env_var = $env_var:literal) => {{
288 use std::{env, path::PathBuf};
289
290 if $self.starts_with($expand) {
291 if let Ok(base_path) = env::var($env_var) {
292 let expanded: PathBuf = $self.components().skip(1).collect();
293 $self = PathBuf::from(base_path).join(expanded)
294 }
295 }
296 }};
297}
298
299pub trait ExpandPaths {
301 fn get_expanded_path(self) -> Option<std::path::PathBuf>;
306}
307
308impl ExpandPaths for std::path::PathBuf {
309 #[cfg(unix)]
313 fn get_expanded_path(mut self) -> Option<Self> {
314 expand_path!(self, "~");
315 expand_path!(self, "$HOME");
316 expand_path!(self, "$XDG_CACHE_HOME", to = ".cache");
317 expand_path!(self, "$XDG_CONFIG_HOME", to = ".config");
318 expand_path!(self, "$XDG_DATA_HOME", to = ".local/share");
319 expand_path!(self, "$XDG_DESKTOP_DIR", to = "Desktop");
320 expand_path!(self, "$XDG_DOCUMENTS_DIR", to = "Documents");
321 expand_path!(self, "$XDG_DOWNLOAD_DIR", to = "Downloads");
322 expand_path!(self, "$XDG_MUSIC_DIR", to = "Music");
323 expand_path!(self, "$XDG_PICTURES_DIR", to = "Pictures");
324 expand_path!(self, "$XDG_PUBLICSHARE_DIR", to = "Public");
325 expand_path!(self, "$XDG_STATE_HOME", to = ".local/state");
326 expand_path!(self, "$XDG_TEMPLATES_DIR", to = "Templates");
327 Some(self)
328 }
329 #[cfg(windows)]
330 fn get_expanded_path(mut self) -> Option<Self> {
331 expand_path!(self, "~");
333 expand_env_path!(self, "%USERPROFILE%", env_var = "USERPROFILE");
334 expand_env_path!(self, "%APPDATA%", env_var = "APPDATA");
335 expand_env_path!(self, "%LOCALAPPDATA%", env_var = "LOCALAPPDATA");
336 expand_env_path!(self, "%TEMP%", env_var = "TEMP");
337 expand_env_path!(self, "%TMP%", env_var = "TMP");
338 expand_path!(self, "%DESKTOP%", to = "Desktop");
339 expand_path!(self, "%DOCUMENTS%", to = "Documents");
340 expand_path!(self, "%DOWNLOADS%", to = "Downloads");
341 expand_path!(self, "%MUSIC%", to = "Music");
342 expand_path!(self, "%PICTURES%", to = "Pictures");
343 expand_path!(self, "%VIDEOS%", to = "Videos");
344 expand_path!(self, "%PUBLIC%", to = "Public");
345 Some(self)
346 }
347}