zen_console_input/
input.rs1use std::io::{self, Write};
4use std::str::FromStr;
5
6pub struct Input {
8 message: String,
9 default: Option<String>,
10 tips: bool,
11 multiline: bool,
12}
13
14impl Input {
15 pub(crate) fn new() -> Self {
16 Input {
17 message: String::new(),
18 default: None,
19 tips: true,
20 multiline: false,
21 }
22 }
23
24 pub fn message(mut self, msg: &str) -> Self {
26 self.message = msg.to_string();
27 self
28 }
29
30 pub fn default(mut self, value: &str) -> Self {
32 self.default = Some(value.to_string());
33 self
34 }
35
36 pub fn disable_tips(mut self) -> Self {
38 self.tips = false;
39 self
40 }
41
42 pub fn multiline(mut self) -> Self {
44 self.multiline = true;
45 self
46 }
47
48 pub fn get_input(&self) -> String {
50 if self.multiline {
51 self.get_multiline_input()
52 } else {
53 self.get_single_line_input()
54 }
55 }
56
57 pub fn get_parsed_input<T: FromStr>(&self) -> T
59 where
60 <T as FromStr>::Err: std::fmt::Debug,
61 {
62 loop {
63 let input = self.get_input();
64 match input.parse() {
65 Ok(value) => return value,
66 Err(_) => {
67 eprintln!("Invalid input. Please try again.");
68 continue;
69 }
70 }
71 }
72 }
73
74 fn get_single_line_input(&self) -> String {
75 print!("{}", self.message);
76 io::stdout().flush().unwrap();
77
78 let mut input = String::new();
79 io::stdin().read_line(&mut input).unwrap();
80 let input = input.trim().to_string();
81
82 if input.is_empty() && self.default.is_some() {
83 self.default.as_ref().unwrap().clone()
84 } else {
85 input
86 }
87 }
88
89 fn get_multiline_input(&self) -> String {
90 println!("{}", self.message);
91 if self.tips {
92 print!("(Press Enter, then Ctrl+D to send)");
93 #[cfg(feature = "editor")]
94 print!(" (In a blank line type @e, then press Enter to edit in external editor)");
95 println!("");
96 }
97
98 let mut input = String::new();
99 loop {
100 let mut line = String::new();
101 let bytes_read = io::stdin().read_line(&mut line);
102
103 match bytes_read {
104 Ok(0) => break, Ok(_) => {
106 #[cfg(feature = "editor")]
107 if line.trim() == "@e" {
108 match super::ZenConsoleInput::edit_in_external_editor(&input) {
109 Ok(edited_content) => {
110 input = edited_content;
111 break;
112 }
113 Err(e) => {
114 println!("Error using external editor: {}", e);
115 }
116 }
117 }
118 input.push_str(&line);
119 }
120 Err(e) => {
121 eprintln!("Error reading input: {}", e);
122 break;
123 }
124 }
125 }
126
127 if input.trim().is_empty() && self.default.is_some() {
128 self.default.as_ref().unwrap().clone()
129 } else {
130 input.trim().to_string()
131 }
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_input_with_default() {
141 let input = Input::new().message("Enter your name").default("John Doe");
142 assert!(input.message.contains("Enter your name"));
143 assert_eq!(input.default, Some("John Doe".to_string()));
144 }
145
146 #[test]
147 fn test_multiline_input() {
148 let input = Input::new().message("Enter your bio").multiline();
149 assert!(input.multiline);
150 }
151}