Skip to main content

git_workspace/
utils.rs

1use anyhow::Context;
2use std::io;
3use std::io::Write;
4use std::path::{Path, PathBuf};
5
6// From https://docs.rs/clt/latest/src/clt/term.rs.html#277-293
7fn build_prompt_text(
8    text: &str,
9    suffix: &str,
10    show_default: bool,
11    default: Option<&str>,
12) -> String {
13    let prompt_text = match (default, show_default) {
14        (Some(default), true) => format!("{} [{}]", text, default),
15        _ => text.to_string(),
16    };
17    prompt_text + suffix
18}
19
20fn get_prompt_input(prompt_text: &str) -> String {
21    print!("{}", prompt_text);
22    io::stdout().flush().unwrap();
23    let mut input = String::new();
24    io::stdin()
25        .read_line(&mut input)
26        .expect("Failed to read line");
27    input.trim_end_matches('\n').to_string()
28}
29
30pub fn confirm(text: &str, default: bool, prompt_suffix: &str, show_default: bool) -> bool {
31    let default_string = match default {
32        true => Some("Y/n"),
33        false => Some("y/N"),
34    };
35    let prompt_text = build_prompt_text(text, prompt_suffix, show_default, default_string);
36
37    loop {
38        let prompt_input = get_prompt_input(&prompt_text).to_ascii_lowercase();
39        match prompt_input.trim() {
40            "y" | "yes" => {
41                return true;
42            }
43            "n" | "no" => {
44                return false;
45            }
46            "" => {
47                return default;
48            }
49            _ => {
50                println!("Error: invalid input");
51            }
52        }
53    }
54}
55
56// Convert our workspace path to a PathBuf. We cannot use the value given directly as
57// it could contain a tilde, so we run `expanduser` on it _if_ we are on a Unix platform.
58// On Windows this isn't supported.
59#[cfg(unix)]
60pub fn expand_workspace_path(path: &Path) -> anyhow::Result<PathBuf> {
61    expanduser::expanduser(path.to_string_lossy())
62        .with_context(|| "Error expanding git workspace path")
63}
64
65#[cfg(not(unix))]
66pub fn expand_workspace_path(path: &Path) -> anyhow::Result<PathBuf> {
67    Ok(path.to_path_buf())
68}
69
70pub fn ensure_workspace_dir_exists(path: &PathBuf) -> anyhow::Result<PathBuf> {
71    if !path.exists() {
72        fs_extra::dir::create_all(path, false)
73            .with_context(|| format!("Error creating workspace directory {}", &path.display()))?;
74    }
75    path.canonicalize()
76        .with_context(|| format!("Error canonicalizing workspace path {}", &path.display()))
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn test_build_prompt_text() {
85        // Test with default and show_default true
86        assert_eq!(
87            build_prompt_text("Continue?", ": ", true, Some("Y/n")),
88            "Continue? [Y/n]: "
89        );
90
91        // Test with default but show_default false
92        assert_eq!(
93            build_prompt_text("Continue?", ": ", false, Some("Y/n")),
94            "Continue?: "
95        );
96
97        // Test without default
98        assert_eq!(
99            build_prompt_text("Continue?", ": ", true, None),
100            "Continue?: "
101        );
102
103        // Test with empty text
104        assert_eq!(build_prompt_text("", ": ", true, Some("Y/n")), " [Y/n]: ");
105    }
106
107    #[test]
108    fn test_expand_workspace_path() {
109        let path = PathBuf::from("/test/path");
110        let result = expand_workspace_path(&path).unwrap();
111        assert_eq!(result, path);
112
113        // Test with relative path
114        let relative_path = PathBuf::from("test/path");
115        let result = expand_workspace_path(&relative_path).unwrap();
116        assert_eq!(result, relative_path);
117    }
118
119    #[test]
120    #[cfg(unix)]
121    fn test_expand_workspace_path_on_unix_platform() {
122        let custom_home = "/custom/home";
123        std::env::set_var("HOME", custom_home);
124
125        let path = PathBuf::from("~/test/path");
126        let result = expand_workspace_path(&path).unwrap();
127        let expected_path = PathBuf::from(format!("{}/test/path", custom_home));
128
129        assert_eq!(result, expected_path);
130        std::env::remove_var("HOME"); // Clean up
131    }
132
133    #[test]
134    fn test_ensure_workspace_exists() {
135        // Test with temporary directory
136        let temp_dir = tempfile::tempdir().unwrap();
137        let path = temp_dir.path().to_path_buf();
138
139        // Test existing directory
140        let result = ensure_workspace_dir_exists(&path).unwrap();
141        assert_eq!(result, path.canonicalize().unwrap());
142
143        // Test non-existing directory
144        let new_path = path.join("new_dir");
145        let result = ensure_workspace_dir_exists(&new_path).unwrap();
146        assert!(new_path.exists());
147        assert_eq!(result, new_path.canonicalize().unwrap());
148    }
149}