sz_rust_cli/
interactive.rs1use std::io::IsTerminal;
9
10use crate::error::CliError;
11
12pub struct InteractivePrompt;
14
15impl InteractivePrompt {
16 pub fn is_tty() -> bool {
20 std::io::stdin().is_terminal()
21 }
22
23 pub fn prompt_fields() -> Result<String, CliError> {
25 if !Self::is_tty() {
26 return Err(CliError::Generic(
27 "not a TTY environment, cannot prompt for fields".to_string(),
28 ));
29 }
30
31 let input = dialoguer::Input::<String>::new()
32 .with_prompt("Enter field definitions (format: name:Type,name2:Type2,...)")
33 .with_initial_text("id:i32:pk,name:String,age:i32")
34 .interact_text();
35
36 match input {
37 Ok(value) => {
38 if value.trim().is_empty() {
39 Err(CliError::Generic(
40 "user entered empty field definition".to_string(),
41 ))
42 } else {
43 Ok(value.trim().to_string())
44 }
45 }
46 Err(e) => Err(CliError::Generic(format!("user cancelled input: {e}"))),
47 }
48 }
49
50 pub fn prompt_table(default: &str) -> Result<String, CliError> {
52 if !Self::is_tty() {
53 return Err(CliError::Generic(
54 "not a TTY environment, cannot prompt for table name".to_string(),
55 ));
56 }
57
58 let input = dialoguer::Input::<String>::new()
59 .with_prompt("Enter table name")
60 .with_initial_text(default)
61 .interact_text();
62
63 match input {
64 Ok(value) => {
65 let value = value.trim().to_string();
66 if value.is_empty() {
67 Ok(default.to_string())
68 } else {
69 Ok(value)
70 }
71 }
72 Err(e) => Err(CliError::Generic(format!("user cancelled input: {e}"))),
73 }
74 }
75
76 pub fn prompt_template(available: &[String]) -> Result<String, CliError> {
78 if available.is_empty() {
79 return Err(CliError::Generic("no templates available".to_string()));
80 }
81
82 if !Self::is_tty() {
83 return Err(CliError::Generic(
84 "not a TTY environment, cannot prompt for template selection".to_string(),
85 ));
86 }
87
88 let select = dialoguer::FuzzySelect::new()
89 .with_prompt("Select template type")
90 .items(available)
91 .interact();
92
93 match select {
94 Ok(idx) => Ok(available[idx].clone()),
95 Err(e) => Err(CliError::Generic(format!("user cancelled selection: {e}"))),
96 }
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn test_is_tty_returns_bool() {
106 let result = InteractivePrompt::is_tty();
107 assert!(result || !result); }
109
110 #[test]
111 fn test_prompt_fields_non_tty() {
112 if !InteractivePrompt::is_tty() {
113 let result = InteractivePrompt::prompt_fields();
114 assert!(result.is_err());
115 let err = result.unwrap_err();
116 assert!(err.to_string().contains("not a TTY"));
117 }
118 }
119
120 #[test]
121 fn test_prompt_table_non_tty() {
122 if !InteractivePrompt::is_tty() {
123 let result = InteractivePrompt::prompt_table("users");
124 assert!(result.is_err());
125 let err = result.unwrap_err();
126 assert!(err.to_string().contains("not a TTY"));
127 }
128 }
129
130 #[test]
131 fn test_prompt_template_non_tty() {
132 if !InteractivePrompt::is_tty() {
133 let result = InteractivePrompt::prompt_template(&[
134 "crud".to_string(),
135 "master-slave".to_string(),
136 ]);
137 assert!(result.is_err());
138 let err = result.unwrap_err();
139 assert!(err.to_string().contains("not a TTY"));
140 }
141 }
142
143 #[test]
144 fn test_prompt_template_empty_list() {
145 let result = InteractivePrompt::prompt_template(&[]);
146 assert!(result.is_err());
147 let err = result.unwrap_err();
148 assert!(err.to_string().contains("no templates"));
149 }
150}