upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/lib/cli_interactor.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/cli_interactor.rs')
-rw-r--r--src/lib/cli_interactor.rs186
1 files changed, 186 insertions, 0 deletions
diff --git a/src/lib/cli_interactor.rs b/src/lib/cli_interactor.rs
new file mode 100644
index 0000000..4cf6357
--- /dev/null
+++ b/src/lib/cli_interactor.rs
@@ -0,0 +1,186 @@
1use anyhow::{Context, Result};
2use dialoguer::{theme::ColorfulTheme, Confirm, Input, Password};
3#[cfg(test)]
4use mockall::*;
5
6#[derive(Default)]
7pub struct Interactor {
8 theme: ColorfulTheme,
9}
10
11#[cfg_attr(test, automock)]
12pub trait InteractorPrompt {
13 fn input(&self, parms: PromptInputParms) -> Result<String>;
14 fn password(&self, parms: PromptPasswordParms) -> Result<String>;
15 fn confirm(&self, params: PromptConfirmParms) -> Result<bool>;
16 fn choice(&self, params: PromptChoiceParms) -> Result<usize>;
17 fn multi_choice(&self, params: PromptMultiChoiceParms) -> Result<Vec<usize>>;
18}
19impl InteractorPrompt for Interactor {
20 fn input(&self, parms: PromptInputParms) -> Result<String> {
21 let mut input = Input::with_theme(&self.theme);
22 input.with_prompt(parms.prompt).allow_empty(parms.optional);
23 if !parms.default.is_empty() {
24 input.default(parms.default);
25 }
26 Ok(input.interact_text()?)
27 }
28 fn password(&self, parms: PromptPasswordParms) -> Result<String> {
29 let mut p = Password::with_theme(&self.theme);
30 p.with_prompt(parms.prompt);
31 if parms.confirm {
32 p.with_confirmation("confirm password", "passwords didnt match...");
33 }
34 let pass: String = p.interact()?;
35 Ok(pass)
36 }
37 fn confirm(&self, params: PromptConfirmParms) -> Result<bool> {
38 let confirm: bool = Confirm::with_theme(&self.theme)
39 .with_prompt(params.prompt)
40 .default(params.default)
41 .interact()?;
42 Ok(confirm)
43 }
44 fn choice(&self, parms: PromptChoiceParms) -> Result<usize> {
45 let mut choice = dialoguer::Select::with_theme(&self.theme);
46 choice
47 .with_prompt(parms.prompt)
48 .report(parms.report)
49 .items(&parms.choices);
50 if let Some(default) = parms.default {
51 if std::env::var("NGITTEST").is_err() {
52 choice.default(default);
53 }
54 }
55 choice.interact().context("failed to get choice")
56 }
57 fn multi_choice(&self, parms: PromptMultiChoiceParms) -> Result<Vec<usize>> {
58 // the colorful theme is not very clear so falling back to default
59 let mut choice = dialoguer::MultiSelect::default();
60 choice
61 .with_prompt(parms.prompt)
62 .report(parms.report)
63 .items(&parms.choices);
64 if let Some(defaults) = parms.defaults {
65 choice.defaults(&defaults);
66 }
67 choice.interact().context("failed to get choice")
68 }
69}
70
71#[derive(Default)]
72pub struct PromptInputParms {
73 pub prompt: String,
74 pub default: String,
75 pub optional: bool,
76}
77
78impl PromptInputParms {
79 pub fn with_prompt<S: Into<String>>(mut self, prompt: S) -> Self {
80 self.prompt = prompt.into();
81 self
82 }
83 pub fn with_default<S: Into<String>>(mut self, default: S) -> Self {
84 self.default = default.into();
85 self
86 }
87 pub fn optional(mut self) -> Self {
88 self.optional = true;
89 self
90 }
91}
92
93#[derive(Default)]
94pub struct PromptPasswordParms {
95 pub prompt: String,
96 pub confirm: bool,
97}
98
99impl PromptPasswordParms {
100 pub fn with_prompt<S: Into<String>>(mut self, prompt: S) -> Self {
101 self.prompt = prompt.into();
102 self
103 }
104 pub const fn with_confirm(mut self) -> Self {
105 self.confirm = true;
106 self
107 }
108}
109
110#[derive(Default)]
111pub struct PromptConfirmParms {
112 pub prompt: String,
113 pub default: bool,
114}
115
116impl PromptConfirmParms {
117 pub fn with_prompt<S: Into<String>>(mut self, prompt: S) -> Self {
118 self.prompt = prompt.into();
119 self
120 }
121 pub fn with_default(mut self, default: bool) -> Self {
122 self.default = default;
123 self
124 }
125}
126
127#[derive(Default)]
128pub struct PromptChoiceParms {
129 pub prompt: String,
130 pub choices: Vec<String>,
131 pub default: Option<usize>,
132 pub report: bool,
133}
134
135impl PromptChoiceParms {
136 pub fn with_prompt<S: Into<String>>(mut self, prompt: S) -> Self {
137 self.prompt = prompt.into();
138 self.report = true;
139 self
140 }
141
142 // pub fn dont_report(mut self) -> Self {
143 // self.report = false;
144 // self
145 // }
146 pub fn with_choices(mut self, choices: Vec<String>) -> Self {
147 self.choices = choices;
148 self
149 }
150
151 pub fn with_default(mut self, index: usize) -> Self {
152 self.default = Some(index);
153 self
154 }
155}
156
157#[derive(Default)]
158pub struct PromptMultiChoiceParms {
159 pub prompt: String,
160 pub choices: Vec<String>,
161 pub defaults: Option<Vec<bool>>,
162 pub report: bool,
163}
164
165impl PromptMultiChoiceParms {
166 pub fn with_prompt<S: Into<String>>(mut self, prompt: S) -> Self {
167 self.prompt = prompt.into();
168 self.report = true;
169 self
170 }
171
172 pub fn dont_report(mut self) -> Self {
173 self.report = false;
174 self
175 }
176
177 pub fn with_choices(mut self, choices: Vec<String>) -> Self {
178 self.choices = choices;
179 self
180 }
181
182 pub fn with_defaults(mut self, defaults: Vec<bool>) -> Self {
183 self.defaults = Some(defaults);
184 self
185 }
186}