Mercurial > crates > nonstick
comparison src/conv.rs @ 77:351bdc13005e
Update the libpam module to work with the new structure.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Sun, 08 Jun 2025 01:03:46 -0400 |
parents | e58d24849e82 |
children | 002adfb98c5c |
comparison
equal
deleted
inserted
replaced
76:e58d24849e82 | 77:351bdc13005e |
---|---|
12 /// | 12 /// |
13 /// The data within each enum value is the prompt (or other information) | 13 /// The data within each enum value is the prompt (or other information) |
14 /// that will be presented to the user. | 14 /// that will be presented to the user. |
15 #[non_exhaustive] | 15 #[non_exhaustive] |
16 pub enum Message<'a> { | 16 pub enum Message<'a> { |
17 MaskedPrompt(&'a MaskedPrompt<'a>), | 17 Prompt(&'a QAndA<'a>), |
18 Prompt(&'a Prompt<'a>), | 18 MaskedPrompt(&'a MaskedQAndA<'a>), |
19 RadioPrompt(&'a RadioPrompt<'a>), | 19 RadioPrompt(&'a RadioQAndA<'a>), |
20 BinaryPrompt(&'a BinaryPrompt<'a>), | 20 BinaryPrompt(&'a BinaryQAndA<'a>), |
21 InfoMsg(&'a InfoMsg<'a>), | 21 Error(&'a ErrorMsg<'a>), |
22 ErrorMsg(&'a ErrorMsg<'a>), | 22 Info(&'a InfoMsg<'a>), |
23 } | 23 } |
24 | 24 |
25 impl Message<'_> { | 25 impl Message<'_> { |
26 /// Sets an error answer on this question, without having to inspect it. | 26 /// Sets an error answer on this question, without having to inspect it. |
27 /// | 27 /// |
31 /// use nonstick::conv::{Message, QAndA}; | 31 /// use nonstick::conv::{Message, QAndA}; |
32 /// use nonstick::ErrorCode; | 32 /// use nonstick::ErrorCode; |
33 /// | 33 /// |
34 /// fn cant_respond(message: Message) { | 34 /// fn cant_respond(message: Message) { |
35 /// match message { | 35 /// match message { |
36 /// Message::InfoMsg(i) => { | 36 /// Message::Info(i) => { |
37 /// eprintln!("fyi, {}", i.question()); | 37 /// eprintln!("fyi, {}", i.question()); |
38 /// i.set_answer(Ok(())) | 38 /// i.set_answer(Ok(())) |
39 /// } | 39 /// } |
40 /// Message::ErrorMsg(e) => { | 40 /// Message::Error(e) => { |
41 /// eprintln!("ERROR: {}", e.question()); | 41 /// eprintln!("ERROR: {}", e.question()); |
42 /// e.set_answer(Ok(())) | 42 /// e.set_answer(Ok(())) |
43 /// } | 43 /// } |
44 /// // We can't answer any questions. | 44 /// // We can't answer any questions. |
45 /// other => other.set_error(ErrorCode::ConversationError), | 45 /// other => other.set_error(ErrorCode::ConversationError), |
46 /// } | 46 /// } |
47 /// } | 47 /// } |
48 pub fn set_error(&self, err: ErrorCode) { | 48 pub fn set_error(&self, err: ErrorCode) { |
49 match self { | 49 match *self { |
50 Message::Prompt(m) => m.set_answer(Err(err)), | |
50 Message::MaskedPrompt(m) => m.set_answer(Err(err)), | 51 Message::MaskedPrompt(m) => m.set_answer(Err(err)), |
51 Message::Prompt(m) => m.set_answer(Err(err)), | |
52 Message::RadioPrompt(m) => m.set_answer(Err(err)), | 52 Message::RadioPrompt(m) => m.set_answer(Err(err)), |
53 Message::BinaryPrompt(m) => m.set_answer(Err(err)), | 53 Message::BinaryPrompt(m) => m.set_answer(Err(err)), |
54 Message::InfoMsg(m) => m.set_answer(Err(err)), | 54 Message::Error(m) => m.set_answer(Err(err)), |
55 Message::ErrorMsg(m) => m.set_answer(Err(err)), | 55 Message::Info(m) => m.set_answer(Err(err)), |
56 } | 56 } |
57 } | 57 } |
58 } | |
59 | |
60 /// A question-and-answer pair that can be communicated in a [`Conversation`]. | |
61 /// | |
62 /// The asking side creates a `QAndA`, then converts it to a [`Message`] | |
63 /// and sends it via a [`Conversation`]. The Conversation then retrieves | |
64 /// the answer to the question (if needed) and sets the response. | |
65 /// Once control returns to the asker, the asker gets the answer from this | |
66 /// `QAndA` and uses it however it wants. | |
67 /// | |
68 /// For a more detailed explanation of how this works, | |
69 /// see [`Conversation::communicate`]. | |
70 pub trait QAndA<'a> { | |
71 /// The type of the content of the question. | |
72 type Question: Copy; | |
73 /// The type of the answer to the question. | |
74 type Answer; | |
75 | |
76 /// Converts this Q-and-A pair into a [`Message`] for the [`Conversation`]. | |
77 fn message(&self) -> Message; | |
78 | |
79 /// The contents of the question being asked. | |
80 /// | |
81 /// For instance, this might say `"Username:"` to prompt the user | |
82 /// for their name. | |
83 fn question(&self) -> Self::Question; | |
84 | |
85 /// Sets the answer to the question. | |
86 /// | |
87 /// The [`Conversation`] implementation calls this to set the answer. | |
88 /// The conversation should *always call this function*, even for messages | |
89 /// that don't have "an answer" (like error or info messages). | |
90 fn set_answer(&self, answer: Result<Self::Answer>); | |
91 | |
92 /// Gets the answer to the question. | |
93 fn answer(self) -> Result<Self::Answer>; | |
94 } | 58 } |
95 | 59 |
96 macro_rules! q_and_a { | 60 macro_rules! q_and_a { |
97 ($name:ident<'a, Q=$qt:ty, A=$at:ty>, $($doc:literal)*) => { | 61 ($name:ident<'a, Q=$qt:ty, A=$at:ty>, $val:path, $($doc:literal)*) => { |
98 $( | 62 $( |
99 #[doc = $doc] | 63 #[doc = $doc] |
100 )* | 64 )* |
101 pub struct $name<'a> { | 65 pub struct $name<'a> { |
102 q: $qt, | 66 q: $qt, |
103 a: Cell<Result<$at>>, | 67 a: Cell<Result<$at>>, |
104 } | 68 } |
105 | 69 |
106 impl<'a> QAndA<'a> for $name<'a> { | 70 impl<'a> $name<'a> { |
107 type Question = $qt; | 71 /// Converts this Q&A into a [`Message`] for the [`Conversation`]. |
108 type Answer = $at; | 72 pub fn message(&self) -> Message { |
109 | 73 $val(self) |
110 fn question(&self) -> Self::Question { | 74 } |
75 | |
76 /// The contents of the question being asked. | |
77 /// | |
78 /// For instance, this might say `"Username:"` to prompt the user | |
79 /// for their name, or the text of an error message. | |
80 pub fn question(&self) -> $qt { | |
111 self.q | 81 self.q |
112 } | 82 } |
113 | 83 |
114 fn set_answer(&self, answer: Result<Self::Answer>) { | 84 /// Sets the answer to the question. |
85 /// | |
86 /// The [`Conversation`] implementation calls this to set the answer. | |
87 /// The conversation should *always call this function*, | |
88 /// even for Q&A messages that don't have "an answer" | |
89 /// (like error or info messages). | |
90 pub fn set_answer(&self, answer: Result<$at>) { | |
115 self.a.set(answer) | 91 self.a.set(answer) |
116 } | 92 } |
117 | 93 |
118 fn answer(self) -> Result<Self::Answer> { | 94 /// Gets the answer to the question. |
95 pub fn answer(self) -> Result<$at> { | |
119 self.a.into_inner() | 96 self.a.into_inner() |
120 } | |
121 | |
122 fn message(&self) -> Message { | |
123 Message::$name(self) | |
124 } | 97 } |
125 } | 98 } |
126 }; | 99 }; |
127 } | 100 } |
128 | 101 |
129 macro_rules! ask { | 102 macro_rules! ask { |
130 ($t:ident) => { | 103 ($t:ident) => { |
131 impl<'a> $t<'a> { | 104 impl<'a> $t<'a> { |
132 #[doc = concat!("Creates a `", stringify!($t), "` to be sent to the user.")] | 105 #[doc = concat!("Creates a `", stringify!($t), "` to be sent to the user.")] |
133 fn ask(question: &'a str) -> Self { | 106 pub fn new(question: &'a str) -> Self { |
134 Self { | 107 Self { |
135 q: question, | 108 q: question, |
136 a: Cell::new(Err(ErrorCode::ConversationError)), | 109 a: Cell::new(Err(ErrorCode::ConversationError)), |
137 } | 110 } |
138 } | 111 } |
139 } | 112 } |
140 }; | 113 }; |
141 } | 114 } |
142 | 115 |
143 q_and_a!( | 116 q_and_a!( |
144 MaskedPrompt<'a, Q=&'a str, A=SecureString>, | 117 MaskedQAndA<'a, Q=&'a str, A=SecureString>, |
145 "Asks the user for data and does not echo it back while being entered." | 118 Message::MaskedPrompt, |
119 "A Q&A that asks the user for text and does not show it while typing." | |
146 "" | 120 "" |
147 "In other words, a password entry prompt." | 121 "In other words, a password entry prompt." |
148 ); | 122 ); |
149 ask!(MaskedPrompt); | 123 ask!(MaskedQAndA); |
150 | 124 |
151 q_and_a!( | 125 q_and_a!( |
152 Prompt<'a, Q=&'a str, A=String>, | 126 QAndA<'a, Q=&'a str, A=String>, |
153 "Asks the user for data." | 127 Message::Prompt, |
128 "A standard Q&A prompt that asks the user for text." | |
154 "" | 129 "" |
155 "This is the normal \"ask a person a question\" prompt." | 130 "This is the normal \"ask a person a question\" prompt." |
156 "When the user types, their input will be shown to them." | 131 "When the user types, their input will be shown to them." |
157 "It can be used for things like usernames." | 132 "It can be used for things like usernames." |
158 ); | 133 ); |
159 ask!(Prompt); | 134 ask!(QAndA); |
160 | 135 |
161 q_and_a!( | 136 q_and_a!( |
162 RadioPrompt<'a, Q=&'a str, A=String>, | 137 RadioQAndA<'a, Q=&'a str, A=String>, |
163 "Asks the user for \"radio button\"–style data. (Linux-PAM extension)" | 138 Message::RadioPrompt, |
139 "A Q&A for \"radio button\"–style data. (Linux-PAM extension)" | |
164 "" | 140 "" |
165 "This message type is theoretically useful for \"yes/no/maybe\"" | 141 "This message type is theoretically useful for \"yes/no/maybe\"" |
166 "questions, but nowhere in the documentation is it specified" | 142 "questions, but nowhere in the documentation is it specified" |
167 "what the format of the answer will be, or how this should be shown." | 143 "what the format of the answer will be, or how this should be shown." |
168 ); | 144 ); |
169 ask!(RadioPrompt); | 145 ask!(RadioQAndA); |
170 | 146 |
171 q_and_a!( | 147 q_and_a!( |
172 BinaryPrompt<'a, Q=BinaryQuestion<'a>, A=BinaryData>, | 148 BinaryQAndA<'a, Q=BorrowedBinaryData<'a>, A=BinaryData>, |
149 Message::BinaryPrompt, | |
173 "Asks for binary data. (Linux-PAM extension)" | 150 "Asks for binary data. (Linux-PAM extension)" |
174 "" | 151 "" |
175 "This sends a binary message to the client application." | 152 "This sends a binary message to the client application." |
176 "It can be used to communicate with non-human logins," | 153 "It can be used to communicate with non-human logins," |
177 "or to enable things like security keys." | 154 "or to enable things like security keys." |
178 "" | 155 "" |
179 "The `data_type` tag is a value that is simply passed through" | 156 "The `data_type` tag is a value that is simply passed through" |
180 "to the application. PAM does not define any meaning for it." | 157 "to the application. PAM does not define any meaning for it." |
181 ); | 158 ); |
182 impl<'a> BinaryPrompt<'a> { | 159 impl<'a> BinaryQAndA<'a> { |
183 /// Creates a prompt for the given binary data. | 160 /// Creates a prompt for the given binary data. |
184 /// | 161 /// |
185 /// The `data_type` is a tag you can use for communication between | 162 /// The `data_type` is a tag you can use for communication between |
186 /// the module and the application. Its meaning is undefined by PAM. | 163 /// the module and the application. Its meaning is undefined by PAM. |
187 fn ask(data: &'a [u8], data_type: u8) -> Self { | 164 pub fn new(data: &'a [u8], data_type: u8) -> Self { |
165 BorrowedBinaryData { data, data_type }.into() | |
166 } | |
167 } | |
168 | |
169 impl<'a> From<BorrowedBinaryData<'a>> for BinaryQAndA<'a> { | |
170 fn from(q: BorrowedBinaryData<'a>) -> Self { | |
188 Self { | 171 Self { |
189 q: BinaryQuestion { data, data_type }, | 172 q, |
190 a: Cell::new(Err(ErrorCode::ConversationError)), | 173 a: Cell::new(Err(ErrorCode::ConversationError)), |
191 } | 174 } |
192 } | 175 } |
193 } | 176 } |
194 | 177 |
195 /// The contents of a question requesting binary data. | 178 impl<'a> From<&'a BinaryData> for BinaryQAndA<'a> { |
196 /// | 179 fn from(src: &'a BinaryData) -> Self { |
197 /// A borrowed version of [`BinaryData`]. | 180 BorrowedBinaryData::from(src).into() |
198 #[derive(Copy, Clone, Debug)] | 181 } |
199 pub struct BinaryQuestion<'a> { | 182 } |
183 | |
184 /// A version of [`BinaryData`] where the `data` is borrowed. | |
185 #[derive(Copy, Clone, Debug, Default)] | |
186 pub struct BorrowedBinaryData<'a> { | |
200 data: &'a [u8], | 187 data: &'a [u8], |
201 data_type: u8, | 188 data_type: u8, |
202 } | 189 } |
203 | 190 |
204 impl BinaryQuestion<'_> { | 191 impl<'a> BorrowedBinaryData<'a> { |
192 /// Creates a new BinaryQuestion as a view over the given data. | |
193 pub fn new(data: &'a [u8], data_type: u8) -> Self { | |
194 Self { data, data_type } | |
195 } | |
196 | |
205 /// Gets the data of this question. | 197 /// Gets the data of this question. |
206 pub fn data(&self) -> &[u8] { | 198 pub fn data(&self) -> &[u8] { |
207 self.data | 199 self.data |
208 } | 200 } |
209 | 201 |
211 pub fn data_type(&self) -> u8 { | 203 pub fn data_type(&self) -> u8 { |
212 self.data_type | 204 self.data_type |
213 } | 205 } |
214 } | 206 } |
215 | 207 |
208 impl<'a> From<&'a BinaryData> for BorrowedBinaryData<'a> { | |
209 fn from(value: &'a BinaryData) -> Self { | |
210 Self::new(&value.data, value.data_type) | |
211 } | |
212 } | |
213 | |
216 /// Owned binary data. | 214 /// Owned binary data. |
217 /// | 215 /// |
218 /// For borrowed data, see [`BinaryQuestion`]. | 216 /// For borrowed data, see [`BorrowedBinaryData`]. |
219 /// You can take ownership of the stored data with `.into::<Vec<u8>>()`. | 217 /// You can take ownership of the stored data with `.into::<Vec<u8>>()`. |
220 #[derive(Debug, PartialEq)] | 218 #[derive(Debug, Default, PartialEq)] |
221 pub struct BinaryData { | 219 pub struct BinaryData { |
222 data: Vec<u8>, | 220 data: Vec<u8>, |
223 data_type: u8, | 221 data_type: u8, |
224 } | 222 } |
225 | 223 |
236 pub fn data_type(&self) -> u8 { | 234 pub fn data_type(&self) -> u8 { |
237 self.data_type | 235 self.data_type |
238 } | 236 } |
239 } | 237 } |
240 | 238 |
239 impl<'a> From<BorrowedBinaryData<'a>> for BinaryData { | |
240 fn from(value: BorrowedBinaryData) -> Self { | |
241 Self { | |
242 data: value.data.to_vec(), | |
243 data_type: value.data_type, | |
244 } | |
245 } | |
246 } | |
247 | |
241 impl From<BinaryData> for Vec<u8> { | 248 impl From<BinaryData> for Vec<u8> { |
242 /// Takes ownership of the data stored herein. | 249 /// Takes ownership of the data stored herein. |
243 fn from(value: BinaryData) -> Self { | 250 fn from(value: BinaryData) -> Self { |
244 value.data | 251 value.data |
245 } | 252 } |
246 } | 253 } |
247 | 254 |
248 q_and_a!( | 255 q_and_a!( |
249 InfoMsg<'a, Q = &'a str, A = ()>, | 256 InfoMsg<'a, Q = &'a str, A = ()>, |
257 Message::Info, | |
250 "A message containing information to be passed to the user." | 258 "A message containing information to be passed to the user." |
251 "" | 259 "" |
252 "While this does not have an answer, [`Conversation`] implementations" | 260 "While this does not have an answer, [`Conversation`] implementations" |
253 "should still call [`set_answer`][`QAndA::set_answer`] to verify that" | 261 "should still call [`set_answer`][`QAndA::set_answer`] to verify that" |
254 "the message has been displayed (or actively discarded)." | 262 "the message has been displayed (or actively discarded)." |
255 ); | 263 ); |
256 impl<'a> InfoMsg<'a> { | 264 impl<'a> InfoMsg<'a> { |
257 /// Creates an informational message to send to the user. | 265 /// Creates an informational message to send to the user. |
258 fn new(message: &'a str) -> Self { | 266 pub fn new(message: &'a str) -> Self { |
259 Self { | 267 Self { |
260 q: message, | 268 q: message, |
261 a: Cell::new(Err(ErrorCode::ConversationError)), | 269 a: Cell::new(Err(ErrorCode::ConversationError)), |
262 } | 270 } |
263 } | 271 } |
264 } | 272 } |
265 | 273 |
266 q_and_a!( | 274 q_and_a!( |
267 ErrorMsg<'a, Q = &'a str, A = ()>, | 275 ErrorMsg<'a, Q = &'a str, A = ()>, |
276 Message::Error, | |
268 "An error message to be passed to the user." | 277 "An error message to be passed to the user." |
269 "" | 278 "" |
270 "While this does not have an answer, [`Conversation`] implementations" | 279 "While this does not have an answer, [`Conversation`] implementations" |
271 "should still call [`set_answer`][`QAndA::set_answer`] to verify that" | 280 "should still call [`set_answer`][`QAndA::set_answer`] to verify that" |
272 "the message has been displayed (or actively discarded)." | 281 "the message has been displayed (or actively discarded)." |
273 | 282 |
274 ); | 283 ); |
275 impl<'a> ErrorMsg<'a> { | 284 impl<'a> ErrorMsg<'a> { |
276 /// Creates an error message to send to the user. | 285 /// Creates an error message to send to the user. |
277 fn new(message: &'a str) -> Self { | 286 pub fn new(message: &'a str) -> Self { |
278 Self { | 287 Self { |
279 q: message, | 288 q: message, |
280 a: Cell::new(Err(ErrorCode::ConversationError)), | 289 a: Cell::new(Err(ErrorCode::ConversationError)), |
281 } | 290 } |
282 } | 291 } |
425 /// Requests binary data from the user (a Linux-PAM extension). | 434 /// Requests binary data from the user (a Linux-PAM extension). |
426 fn binary_prompt(&mut self, data: &[u8], data_type: u8) -> Result<BinaryData>; | 435 fn binary_prompt(&mut self, data: &[u8], data_type: u8) -> Result<BinaryData>; |
427 } | 436 } |
428 | 437 |
429 macro_rules! conv_fn { | 438 macro_rules! conv_fn { |
430 ($fn_name:ident($($param:ident: $pt:ty),+) -> $resp_type:ty { $ask:path }) => { | 439 ($fn_name:ident($($param:ident: $pt:ty),+) -> $resp_type:ty { $msg:ty }) => { |
431 fn $fn_name(&mut self, $($param: $pt),*) -> Result<$resp_type> { | 440 fn $fn_name(&mut self, $($param: $pt),*) -> Result<$resp_type> { |
432 let prompt = $ask($($param),*); | 441 let prompt = <$msg>::new($($param),*); |
433 self.communicate(&[prompt.message()]); | 442 self.communicate(&[prompt.message()]); |
434 prompt.answer() | 443 prompt.answer() |
435 } | 444 } |
436 }; | 445 }; |
437 ($fn_name:ident($($param:ident: $pt:ty),+) { $ask:path }) => { | 446 ($fn_name:ident($($param:ident: $pt:ty),+) { $msg:ty }) => { |
438 fn $fn_name(&mut self, $($param: $pt),*) { | 447 fn $fn_name(&mut self, $($param: $pt),*) { |
439 self.communicate(&[$ask($($param),*).message()]); | 448 self.communicate(&[<$msg>::new($($param),*).message()]); |
440 } | 449 } |
441 }; | 450 }; |
442 } | 451 } |
443 | 452 |
444 impl<C: Conversation> SimpleConversation for C { | 453 impl<C: Conversation> SimpleConversation for C { |
445 conv_fn!(prompt(message: &str) -> String { Prompt::ask }); | 454 conv_fn!(prompt(message: &str) -> String { QAndA }); |
446 conv_fn!(masked_prompt(message: &str) -> SecureString { MaskedPrompt::ask }); | 455 conv_fn!(masked_prompt(message: &str) -> SecureString { MaskedQAndA } ); |
447 conv_fn!(radio_prompt(message: &str) -> String { RadioPrompt::ask }); | 456 conv_fn!(radio_prompt(message: &str) -> String { RadioQAndA }); |
448 conv_fn!(error_msg(message: &str) { ErrorMsg::new }); | 457 conv_fn!(error_msg(message: &str) { ErrorMsg }); |
449 conv_fn!(info_msg(message: &str) { InfoMsg::new }); | 458 conv_fn!(info_msg(message: &str) { InfoMsg }); |
450 conv_fn!(binary_prompt(data: &[u8], data_type: u8) -> BinaryData { BinaryPrompt::ask }); | 459 conv_fn!(binary_prompt(data: &[u8], data_type: u8) -> BinaryData { BinaryQAndA }); |
451 } | 460 } |
452 | 461 |
453 /// A [`Conversation`] which asks the questions one at a time. | 462 /// A [`Conversation`] which asks the questions one at a time. |
454 /// | 463 /// |
455 /// This is automatically created by [`SimpleConversation::as_conversation`]. | 464 /// This is automatically created by [`SimpleConversation::as_conversation`]. |
464 prompt.set_answer(self.0.masked_prompt(prompt.question())) | 473 prompt.set_answer(self.0.masked_prompt(prompt.question())) |
465 } | 474 } |
466 Message::RadioPrompt(prompt) => { | 475 Message::RadioPrompt(prompt) => { |
467 prompt.set_answer(self.0.radio_prompt(prompt.question())) | 476 prompt.set_answer(self.0.radio_prompt(prompt.question())) |
468 } | 477 } |
469 Message::InfoMsg(prompt) => { | 478 Message::Info(prompt) => { |
470 self.0.info_msg(prompt.question()); | 479 self.0.info_msg(prompt.question()); |
471 prompt.set_answer(Ok(())) | 480 prompt.set_answer(Ok(())) |
472 } | 481 } |
473 Message::ErrorMsg(prompt) => { | 482 Message::Error(prompt) => { |
474 self.0.error_msg(prompt.question()); | 483 self.0.error_msg(prompt.question()); |
475 prompt.set_answer(Ok(())) | 484 prompt.set_answer(Ok(())) |
476 } | 485 } |
477 Message::BinaryPrompt(prompt) => { | 486 Message::BinaryPrompt(prompt) => { |
478 let q = prompt.question(); | 487 let q = prompt.question(); |
484 } | 493 } |
485 | 494 |
486 #[cfg(test)] | 495 #[cfg(test)] |
487 mod tests { | 496 mod tests { |
488 use super::{ | 497 use super::{ |
489 BinaryPrompt, Conversation, ErrorMsg, InfoMsg, MaskedPrompt, Message, Prompt, QAndA, | 498 BinaryQAndA, Conversation, ErrorMsg, InfoMsg, MaskedQAndA, Message, QAndA, |
490 RadioPrompt, Result, SecureString, SimpleConversation, | 499 RadioQAndA, Result, SecureString, SimpleConversation, |
491 }; | 500 }; |
492 use crate::constants::ErrorCode; | 501 use crate::constants::ErrorCode; |
493 use crate::BinaryData; | 502 use crate::BinaryData; |
494 | 503 |
495 #[test] | 504 #[test] |
531 } | 540 } |
532 } | 541 } |
533 | 542 |
534 let mut tester = DemuxTester::default(); | 543 let mut tester = DemuxTester::default(); |
535 | 544 |
536 let what = Prompt::ask("what"); | 545 let what = QAndA::new("what"); |
537 let pass = MaskedPrompt::ask("reveal"); | 546 let pass = MaskedQAndA::new("reveal"); |
538 let err = ErrorMsg::new("whoopsie"); | 547 let err = ErrorMsg::new("whoopsie"); |
539 let info = InfoMsg::new("did you know"); | 548 let info = InfoMsg::new("did you know"); |
540 let has_err = Prompt::ask("give_err"); | 549 let has_err = QAndA::new("give_err"); |
541 | 550 |
542 let mut conv = tester.as_conversation(); | 551 let mut conv = tester.as_conversation(); |
543 | 552 |
544 // Basic tests. | 553 // Basic tests. |
545 | 554 |
561 | 570 |
562 // Test the Linux extensions separately. | 571 // Test the Linux extensions separately. |
563 | 572 |
564 let mut conv = tester.as_conversation(); | 573 let mut conv = tester.as_conversation(); |
565 | 574 |
566 let radio = RadioPrompt::ask("channel?"); | 575 let radio = RadioQAndA::new("channel?"); |
567 let bin = BinaryPrompt::ask(&[10, 9, 8], 66); | 576 let bin = BinaryQAndA::new(&[10, 9, 8], 66); |
568 conv.communicate(&[radio.message(), bin.message()]); | 577 conv.communicate(&[radio.message(), bin.message()]); |
569 | 578 |
570 assert_eq!("zero", radio.answer().unwrap()); | 579 assert_eq!("zero", radio.answer().unwrap()); |
571 assert_eq!(BinaryData::new(vec![5, 5, 5], 5), bin.answer().unwrap()); | 580 assert_eq!(BinaryData::new(vec![5, 5, 5], 5), bin.answer().unwrap()); |
572 } | 581 } |
576 | 585 |
577 impl Conversation for MuxTester { | 586 impl Conversation for MuxTester { |
578 fn communicate(&mut self, messages: &[Message]) { | 587 fn communicate(&mut self, messages: &[Message]) { |
579 if let [msg] = messages { | 588 if let [msg] = messages { |
580 match *msg { | 589 match *msg { |
581 Message::InfoMsg(info) => { | 590 Message::Info(info) => { |
582 assert_eq!("let me tell you", info.question()); | 591 assert_eq!("let me tell you", info.question()); |
583 info.set_answer(Ok(())) | 592 info.set_answer(Ok(())) |
584 } | 593 } |
585 Message::ErrorMsg(error) => { | 594 Message::Error(error) => { |
586 assert_eq!("oh no", error.question()); | 595 assert_eq!("oh no", error.question()); |
587 error.set_answer(Ok(())) | 596 error.set_answer(Ok(())) |
588 } | 597 } |
589 Message::Prompt(prompt) => prompt.set_answer(match prompt.question() { | 598 Message::Prompt(prompt) => prompt.set_answer(match prompt.question() { |
590 "should_err" => Err(ErrorCode::PermissionDenied), | 599 "should_err" => Err(ErrorCode::PermissionDenied), |