1use core::fmt;
8
9use inquire::{Confirm, InquireError, MultiSelect, Password, PasswordDisplayMode, Select, Text};
10use secrecy::SecretString;
11use thiserror::Error;
12
13use crate::validator::{U16Validator, UsizeValidator};
14
15#[derive(Debug, Error)]
17pub enum PromptError {
18 #[error("cannot prompt unsigned integer (u16)")]
20 U16(#[source] InquireError),
21 #[error("cannot prompt unsigned integer (usize)")]
23 Usize(#[source] InquireError),
24 #[error("cannot prompt secret")]
26 Secret(#[source] InquireError),
27 #[error("cannot prompt password")]
29 Password(#[source] InquireError),
30 #[error("cannot prompt text")]
32 Text(#[source] InquireError),
33 #[error("cannot prompt boolean")]
35 Bool(#[source] InquireError),
36 #[error("cannot prompt item from list")]
38 Item(#[source] InquireError),
39 #[error("cannot prompt items from list")]
41 Items(#[source] InquireError),
42}
43
44pub type PromptResult<T> = Result<T, PromptError>;
46
47pub fn u16(prompt: impl AsRef<str>, default: Option<u16>) -> PromptResult<u16> {
49 let prompt = Text::new(prompt.as_ref()).with_validator(U16Validator);
50
51 let number = if let Some(default) = default {
52 prompt.with_default(&default.to_string()).prompt()
53 } else {
54 prompt.prompt()
55 };
56
57 match number {
58 Ok(number) => Ok(number.parse().unwrap()),
59 Err(err) => Err(PromptError::U16(err)),
60 }
61}
62
63pub fn usize(prompt: impl AsRef<str>, default: Option<usize>) -> PromptResult<usize> {
65 let prompt = Text::new(prompt.as_ref()).with_validator(UsizeValidator);
66
67 let number = if let Some(default) = default {
68 prompt.with_default(&default.to_string()).prompt()
69 } else {
70 prompt.prompt()
71 };
72
73 match number {
74 Ok(number) => Ok(number.parse().unwrap()),
75 Err(err) => Err(PromptError::Usize(err)),
76 }
77}
78
79pub fn secret(prompt: impl AsRef<str>) -> PromptResult<String> {
81 Password::new(prompt.as_ref())
82 .with_display_mode(PasswordDisplayMode::Masked)
83 .without_confirmation()
84 .prompt()
85 .map_err(PromptError::Secret)
86}
87
88pub fn some_secret(prompt: impl AsRef<str>) -> PromptResult<Option<SecretString>> {
90 Password::new(prompt.as_ref())
91 .with_display_mode(PasswordDisplayMode::Masked)
92 .without_confirmation()
93 .prompt_skippable()
94 .map(|secret| secret.map(Into::into))
95 .map_err(PromptError::Secret)
96}
97
98pub fn password(prompt: impl AsRef<str>, confirm: impl AsRef<str>) -> PromptResult<SecretString> {
100 Password::new(prompt.as_ref())
101 .with_display_mode(PasswordDisplayMode::Masked)
102 .with_custom_confirmation_message(confirm.as_ref())
103 .prompt()
104 .map(Into::into)
105 .map_err(PromptError::Password)
106}
107
108pub fn text<T: AsRef<str>>(prompt: T, default: Option<T>) -> PromptResult<String> {
110 let mut prompt = Text::new(prompt.as_ref());
111
112 if let Some(default) = default.as_ref() {
113 prompt = prompt.with_default(default.as_ref())
114 }
115
116 prompt.prompt().map_err(PromptError::Text)
117}
118
119pub fn some_text<T: AsRef<str>>(prompt: T, default: Option<T>) -> PromptResult<Option<String>> {
121 let mut prompt = Text::new(prompt.as_ref());
122
123 if let Some(default) = default.as_ref() {
124 prompt = prompt.with_default(default.as_ref())
125 }
126
127 prompt.prompt_skippable().map_err(PromptError::Text)
128}
129
130pub fn bool(prompt: impl AsRef<str>, default: bool) -> PromptResult<bool> {
132 Confirm::new(prompt.as_ref())
133 .with_default(default)
134 .prompt()
135 .map_err(PromptError::Bool)
136}
137
138pub fn item<T: fmt::Display + Eq>(
141 prompt: impl AsRef<str>,
142 items: impl IntoIterator<Item = T>,
143 default: Option<T>,
144) -> PromptResult<T> {
145 let items: Vec<_> = items.into_iter().collect();
146
147 let default = if let Some(default) = default.as_ref() {
148 items
149 .iter()
150 .enumerate()
151 .find_map(|(i, item)| if item == default { Some(i) } else { None })
152 } else {
153 None
154 };
155
156 let mut prompt = Select::new(prompt.as_ref(), items).without_filtering();
157
158 if let Some(default) = default.as_ref() {
159 prompt = prompt.with_starting_cursor(*default);
160 }
161
162 prompt.prompt().map_err(PromptError::Item)
163}
164
165pub fn items<T: fmt::Display + Eq>(
168 prompt: impl AsRef<str>,
169 items: impl IntoIterator<Item = T>,
170 default: impl IntoIterator<Item = usize>,
171) -> PromptResult<Vec<T>> {
172 let items: Vec<_> = items.into_iter().collect();
173 let default: Vec<usize> = default.into_iter().collect();
174
175 MultiSelect::new(prompt.as_ref(), items)
176 .without_filtering()
177 .with_default(&default)
178 .prompt()
179 .map_err(PromptError::Items)
180}