Skip to main content

custom_utils/util_args/
mod.rs

1use anyhow::Context;
2use std::path::PathBuf;
3
4/// arg_value("--check", "-c")
5pub fn arg_value(long: &str, short: &str) -> Option<String> {
6    assert!(long.starts_with("--"));
7    assert!(short.starts_with('-'));
8    let mut is_val = false;
9    for arg in std::env::args() {
10        if is_val {
11            return Some(arg);
12        }
13        is_val = arg == long || arg == short;
14    }
15    None
16}
17
18/// exist_arg("--check", "-c")
19pub fn exist_arg(long: &str, short: &str) -> bool {
20    assert!(long.starts_with("--"));
21    assert!(short.starts_with('-'));
22    for arg in std::env::args() {
23        if arg == long || arg == short {
24            return true;
25        }
26    }
27    false
28}
29
30pub fn command() -> Option<String> {
31    for (index, arg) in std::env::args().enumerate() {
32        if index == 1 {
33            return Some(arg);
34        }
35    }
36    None
37}
38
39/// Gets the current system user's home directory.
40pub fn get_user_home() -> anyhow::Result<PathBuf> {
41    home::home_dir().context("Failed to get user home directory")
42}
43
44/// Expands path, supporting '~' for the user's home directory.
45pub fn expand_path(path: &str) -> anyhow::Result<PathBuf> {
46    #[cfg(not(windows))]
47    {
48        let expanded = shellexpand::tilde(path);
49        return Ok(PathBuf::from(expanded.into_owned()));
50    }
51
52    #[cfg(windows)]
53    {
54        if let Some(stripped) = path.strip_prefix('~') {
55            let home = get_user_home()?;
56            // Remove leading separator if present to avoid join issues
57            let suffix = stripped.trim_start_matches(['/', '\\']);
58            Ok(home.join(suffix))
59        } else if let Some(stripped) = path.strip_prefix("./").or_else(|| path.strip_prefix(".\\")) {
60            let current_dir = std::env::current_dir().context("Failed to get current directory")?;
61            // On Windows, if the remaining part starts with a separator,
62            // PathBuf::join might treat it as an absolute path from the drive root.
63            // We strip all leading separators to ensure it's joined as a relative path.
64            let suffix = stripped.trim_start_matches(['/', '\\']);
65            Ok(current_dir.join(suffix))
66        } else if path == "." {
67            std::env::current_dir().context("Failed to get current directory")
68        } else {
69            Ok(PathBuf::from(path))
70        }
71    }
72}
73
74/// $HOME/.config/app
75pub fn workspace(workspace: &Option<String>, app: &str) -> anyhow::Result<PathBuf> {
76    let workspace = if let Some(workspace) = workspace {
77        expand_path(workspace)?
78    } else {
79        get_user_home()?.join(".config").join(app)
80    };
81    log::debug!("{}", workspace.display());
82    Ok(workspace)
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88    use std::env;
89
90    #[test]
91    fn test_get_user_home() {
92        let home = get_user_home();
93        assert!(home.is_ok());
94    }
95
96    #[test]
97    fn test_expand_path_tilde() {
98        let home = get_user_home().unwrap();
99        let expanded = expand_path("~").unwrap();
100        assert_eq!(expanded, home);
101    }
102
103    #[test]
104    fn test_expand_path_dot() {
105        let cwd = std::env::current_dir().unwrap();
106        let expanded = expand_path("./test").unwrap();
107        assert_eq!(expanded, cwd.join("test"));
108    }
109}