1use anyhow::Context;
2use std::io;
3use std::io::Write;
4use std::path::{Path, PathBuf};
5
6fn 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#[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 assert_eq!(
87 build_prompt_text("Continue?", ": ", true, Some("Y/n")),
88 "Continue? [Y/n]: "
89 );
90
91 assert_eq!(
93 build_prompt_text("Continue?", ": ", false, Some("Y/n")),
94 "Continue?: "
95 );
96
97 assert_eq!(
99 build_prompt_text("Continue?", ": ", true, None),
100 "Continue?: "
101 );
102
103 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 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"); }
132
133 #[test]
134 fn test_ensure_workspace_exists() {
135 let temp_dir = tempfile::tempdir().unwrap();
137 let path = temp_dir.path().to_path_buf();
138
139 let result = ensure_workspace_dir_exists(&path).unwrap();
141 assert_eq!(result, path.canonicalize().unwrap());
142
143 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}