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 = if default.is_some() && show_default {
14 format!("{} [{}]", text, default.unwrap())
15 } else {
16 text.to_string()
17 };
18 prompt_text + suffix
19}
20
21fn get_prompt_input(prompt_text: &str) -> String {
22 print!("{}", prompt_text);
23 io::stdout().flush().unwrap();
24 let mut input = String::new();
25 io::stdin()
26 .read_line(&mut input)
27 .expect("Failed to read line");
28 input.trim_end_matches('\n').to_string()
29}
30
31pub fn confirm(text: &str, default: bool, prompt_suffix: &str, show_default: bool) -> bool {
32 let default_string = match default {
33 true => Some("Y/n"),
34 false => Some("y/N"),
35 };
36 let prompt_text = build_prompt_text(text, prompt_suffix, show_default, default_string);
37
38 loop {
39 let prompt_input = get_prompt_input(&prompt_text).to_ascii_lowercase();
40 match prompt_input.trim() {
41 "y" | "yes" => {
42 return true;
43 }
44 "n" | "no" => {
45 return false;
46 }
47 "" => {
48 return default;
49 }
50 _ => {
51 println!("Error: invalid input");
52 }
53 }
54 }
55}
56
57#[cfg(unix)]
61pub fn expand_workspace_path(path: &Path) -> anyhow::Result<PathBuf> {
62 expanduser::expanduser(path.to_string_lossy())
63 .with_context(|| "Error expanding git workspace path")
64}
65
66#[cfg(not(unix))]
67pub fn expand_workspace_path(path: &Path) -> anyhow::Result<PathBuf> {
68 Ok(path.to_path_buf())
69}
70
71pub fn ensure_workspace_dir_exists(path: &PathBuf) -> anyhow::Result<PathBuf> {
72 if !path.exists() {
73 fs_extra::dir::create_all(path, false)
74 .with_context(|| format!("Error creating workspace directory {}", &path.display()))?;
75 }
76 path.canonicalize()
77 .with_context(|| format!("Error canonicalizing workspace path {}", &path.display()))
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn test_build_prompt_text() {
86 assert_eq!(
88 build_prompt_text("Continue?", ": ", true, Some("Y/n")),
89 "Continue? [Y/n]: "
90 );
91
92 assert_eq!(
94 build_prompt_text("Continue?", ": ", false, Some("Y/n")),
95 "Continue?: "
96 );
97
98 assert_eq!(
100 build_prompt_text("Continue?", ": ", true, None),
101 "Continue?: "
102 );
103
104 assert_eq!(build_prompt_text("", ": ", true, Some("Y/n")), " [Y/n]: ");
106 }
107
108 #[test]
109 fn test_expand_workspace_path() {
110 let path = PathBuf::from("/test/path");
111 let result = expand_workspace_path(&path).unwrap();
112 assert_eq!(result, path);
113
114 let relative_path = PathBuf::from("test/path");
116 let result = expand_workspace_path(&relative_path).unwrap();
117 assert_eq!(result, relative_path);
118 }
119
120 #[test]
121 #[cfg(unix)]
122 fn test_expand_workspace_path_on_unix_platform() {
123 let custom_home = "/custom/home";
124 std::env::set_var("HOME", custom_home);
125
126 let path = PathBuf::from("~/test/path");
127 let result = expand_workspace_path(&path).unwrap();
128 let expected_path = PathBuf::from(format!("{}/test/path", custom_home));
129
130 assert_eq!(result, expected_path);
131 std::env::remove_var("HOME"); }
133
134 #[test]
135 fn test_ensure_workspace_exists() {
136 let temp_dir = tempfile::tempdir().unwrap();
138 let path = temp_dir.path().to_path_buf();
139
140 let result = ensure_workspace_dir_exists(&path).unwrap();
142 assert_eq!(result, path.canonicalize().unwrap());
143
144 let new_path = path.join("new_dir");
146 let result = ensure_workspace_dir_exists(&new_path).unwrap();
147 assert!(new_path.exists());
148 assert_eq!(result, new_path.canonicalize().unwrap());
149 }
150}