garden/
path.rs

1use crate::{constants, errors};
2
3/// Return the current directory as a PathBuf.
4pub fn current_dir() -> std::path::PathBuf {
5    std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from(constants::DOT))
6}
7
8/// Return the current directory as a string.
9pub(crate) fn current_dir_string() -> String {
10    current_dir().to_string_lossy().to_string()
11}
12
13/// Return the home directory for the current user.
14pub(crate) fn home_dir() -> std::path::PathBuf {
15    dirs::home_dir().unwrap_or_else(|| std::path::PathBuf::from("/tmp"))
16}
17
18/// Canonicalize a path while avoiding problematic UNC paths on Windows.
19pub(crate) fn canonicalize<P: AsRef<std::path::Path>>(
20    path: P,
21) -> std::io::Result<std::path::PathBuf> {
22    dunce::canonicalize(path)
23}
24
25/// Convert a Path into an absolute path. Return the original path if it cannot be canonicalized.
26pub fn abspath<P: AsRef<std::path::Path> + std::marker::Copy>(path: P) -> std::path::PathBuf {
27    canonicalize(path).unwrap_or(path.as_ref().to_path_buf())
28}
29
30/// Return the basename of a path-like string.
31pub(crate) fn str_basename(path: &str) -> &str {
32    let basename = if path.contains('/') {
33        path.split('/').next_back().unwrap_or(path)
34    } else if path.contains('\\') {
35        path.split('\\').next_back().unwrap_or(path)
36    } else {
37        path
38    };
39
40    basename
41}
42
43/// Return true if the basename is a known shell.
44pub(crate) fn is_shell(basename: &str) -> bool {
45    matches!(
46        basename,
47        constants::SHELL_BASH
48            | constants::SHELL_DASH
49            | constants::SHELL_KSH
50            | constants::SHELL_SH
51            | constants::SHELL_ZSH
52    )
53}
54
55/// Strip a prefix from a path.
56pub(crate) fn strip_prefix(
57    root: &std::path::Path,
58    path: &std::path::Path,
59) -> Result<std::path::PathBuf, errors::GardenError> {
60    let stripped_path = if path.starts_with(root) {
61        // Is the path a child of the current garden root?
62        path.strip_prefix(root)
63            .map_err(|err| {
64                errors::GardenError::ConfigurationError(format!(
65                    "{path:?} is not a child of {root:?}: {err:?}"
66                ))
67            })?
68            .to_path_buf()
69    } else {
70        path.to_path_buf()
71    };
72
73    Ok(stripped_path)
74}
75
76/// Strip a prefix from a path. Returns a path as a string.
77pub(crate) fn strip_prefix_into_string(
78    root: &std::path::Path,
79    path: &std::path::Path,
80) -> Result<String, errors::GardenError> {
81    let path_str = strip_prefix(root, path)?.to_string_lossy().to_string();
82    Ok(path_str)
83}