Mercurial > crates > nonstick
comparison src/conv.rs @ 78:002adfb98c5c
Rename files, reorder structs, remove annoying BorrowedBinaryData type.
This is basically a cleanup change. Also it adds tests.
- Renames the files with Questions and Answers to question and answer.
- Reorders the structs in those files to put the important ones first.
- Removes the BorrowedBinaryData type. It was a bad idea all along.
Instead, we just use (&[u8], u8).
- Adds some tests because I just can't help myself.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Sun, 08 Jun 2025 03:48:40 -0400 |
parents | 351bdc13005e |
children | 2128123b9406 |
comparison
equal
deleted
inserted
replaced
77:351bdc13005e | 78:002adfb98c5c |
---|---|
1 //! The PAM conversation and associated Stuff. | 1 //! The PAM conversation and associated Stuff. |
2 | 2 |
3 // Temporarily allowed until we get the actual conversation functions hooked up. | 3 // Temporarily allowed until we get the actual conversation functions hooked up. |
4 #![allow(dead_code)] | 4 #![allow(dead_code)] |
5 | 5 |
6 use crate::constants::Result; | 6 use crate::constants::{ErrorCode, Result}; |
7 use crate::ErrorCode; | |
8 use secure_string::SecureString; | 7 use secure_string::SecureString; |
9 use std::cell::Cell; | 8 use std::cell::Cell; |
9 use std::fmt; | |
10 use std::result::Result as StdResult; | |
10 | 11 |
11 /// The types of message and request that can be sent to a user. | 12 /// The types of message and request that can be sent to a user. |
12 /// | 13 /// |
13 /// The data within each enum value is the prompt (or other information) | 14 /// The data within each enum value is the prompt (or other information) |
14 /// that will be presented to the user. | 15 /// that will be presented to the user. |
66 q: $qt, | 67 q: $qt, |
67 a: Cell<Result<$at>>, | 68 a: Cell<Result<$at>>, |
68 } | 69 } |
69 | 70 |
70 impl<'a> $name<'a> { | 71 impl<'a> $name<'a> { |
72 #[doc = concat!("Creates a `", stringify!($t), "` to be sent to the user.")] | |
73 pub fn new(question: $qt) -> Self { | |
74 Self { | |
75 q: question, | |
76 a: Cell::new(Err(ErrorCode::ConversationError)), | |
77 } | |
78 } | |
79 | |
71 /// Converts this Q&A into a [`Message`] for the [`Conversation`]. | 80 /// Converts this Q&A into a [`Message`] for the [`Conversation`]. |
72 pub fn message(&self) -> Message { | 81 pub fn message(&self) -> Message<'_> { |
73 $val(self) | 82 $val(self) |
74 } | 83 } |
75 | 84 |
76 /// The contents of the question being asked. | 85 /// The contents of the question being asked. |
77 /// | 86 /// |
94 /// Gets the answer to the question. | 103 /// Gets the answer to the question. |
95 pub fn answer(self) -> Result<$at> { | 104 pub fn answer(self) -> Result<$at> { |
96 self.a.into_inner() | 105 self.a.into_inner() |
97 } | 106 } |
98 } | 107 } |
99 }; | 108 |
100 } | 109 // shout out to stackoverflow user ballpointben for this lazy impl: |
101 | 110 // https://stackoverflow.com/a/78871280/39808 |
102 macro_rules! ask { | 111 impl fmt::Debug for $name<'_> { |
103 ($t:ident) => { | 112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> StdResult<(), fmt::Error> { |
104 impl<'a> $t<'a> { | 113 #[derive(Debug)] |
105 #[doc = concat!("Creates a `", stringify!($t), "` to be sent to the user.")] | 114 struct $name<'a> { q: $qt } |
106 pub fn new(question: &'a str) -> Self { | 115 fmt::Debug::fmt(&$name { q: self.q }, f) |
107 Self { | |
108 q: question, | |
109 a: Cell::new(Err(ErrorCode::ConversationError)), | |
110 } | |
111 } | 116 } |
112 } | 117 } |
113 }; | 118 }; |
114 } | 119 } |
115 | 120 |
118 Message::MaskedPrompt, | 123 Message::MaskedPrompt, |
119 "A Q&A that asks the user for text and does not show it while typing." | 124 "A Q&A that asks the user for text and does not show it while typing." |
120 "" | 125 "" |
121 "In other words, a password entry prompt." | 126 "In other words, a password entry prompt." |
122 ); | 127 ); |
123 ask!(MaskedQAndA); | |
124 | 128 |
125 q_and_a!( | 129 q_and_a!( |
126 QAndA<'a, Q=&'a str, A=String>, | 130 QAndA<'a, Q=&'a str, A=String>, |
127 Message::Prompt, | 131 Message::Prompt, |
128 "A standard Q&A prompt that asks the user for text." | 132 "A standard Q&A prompt that asks the user for text." |
129 "" | 133 "" |
130 "This is the normal \"ask a person a question\" prompt." | 134 "This is the normal \"ask a person a question\" prompt." |
131 "When the user types, their input will be shown to them." | 135 "When the user types, their input will be shown to them." |
132 "It can be used for things like usernames." | 136 "It can be used for things like usernames." |
133 ); | 137 ); |
134 ask!(QAndA); | |
135 | 138 |
136 q_and_a!( | 139 q_and_a!( |
137 RadioQAndA<'a, Q=&'a str, A=String>, | 140 RadioQAndA<'a, Q=&'a str, A=String>, |
138 Message::RadioPrompt, | 141 Message::RadioPrompt, |
139 "A Q&A for \"radio button\"–style data. (Linux-PAM extension)" | 142 "A Q&A for \"radio button\"–style data. (Linux-PAM extension)" |
140 "" | 143 "" |
141 "This message type is theoretically useful for \"yes/no/maybe\"" | 144 "This message type is theoretically useful for \"yes/no/maybe\"" |
142 "questions, but nowhere in the documentation is it specified" | 145 "questions, but nowhere in the documentation is it specified" |
143 "what the format of the answer will be, or how this should be shown." | 146 "what the format of the answer will be, or how this should be shown." |
144 ); | 147 ); |
145 ask!(RadioQAndA); | |
146 | 148 |
147 q_and_a!( | 149 q_and_a!( |
148 BinaryQAndA<'a, Q=BorrowedBinaryData<'a>, A=BinaryData>, | 150 BinaryQAndA<'a, Q=(&'a [u8], u8), A=BinaryData>, |
149 Message::BinaryPrompt, | 151 Message::BinaryPrompt, |
150 "Asks for binary data. (Linux-PAM extension)" | 152 "Asks for binary data. (Linux-PAM extension)" |
151 "" | 153 "" |
152 "This sends a binary message to the client application." | 154 "This sends a binary message to the client application." |
153 "It can be used to communicate with non-human logins," | 155 "It can be used to communicate with non-human logins," |
154 "or to enable things like security keys." | 156 "or to enable things like security keys." |
155 "" | 157 "" |
156 "The `data_type` tag is a value that is simply passed through" | 158 "The `data_type` tag is a value that is simply passed through" |
157 "to the application. PAM does not define any meaning for it." | 159 "to the application. PAM does not define any meaning for it." |
158 ); | 160 ); |
159 impl<'a> BinaryQAndA<'a> { | |
160 /// Creates a prompt for the given binary data. | |
161 /// | |
162 /// The `data_type` is a tag you can use for communication between | |
163 /// the module and the application. Its meaning is undefined by PAM. | |
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 { | |
171 Self { | |
172 q, | |
173 a: Cell::new(Err(ErrorCode::ConversationError)), | |
174 } | |
175 } | |
176 } | |
177 | |
178 impl<'a> From<&'a BinaryData> for BinaryQAndA<'a> { | |
179 fn from(src: &'a BinaryData) -> Self { | |
180 BorrowedBinaryData::from(src).into() | |
181 } | |
182 } | |
183 | |
184 /// A version of [`BinaryData`] where the `data` is borrowed. | |
185 #[derive(Copy, Clone, Debug, Default)] | |
186 pub struct BorrowedBinaryData<'a> { | |
187 data: &'a [u8], | |
188 data_type: u8, | |
189 } | |
190 | |
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 | |
197 /// Gets the data of this question. | |
198 pub fn data(&self) -> &[u8] { | |
199 self.data | |
200 } | |
201 | |
202 /// Gets the "type" of this data. | |
203 pub fn data_type(&self) -> u8 { | |
204 self.data_type | |
205 } | |
206 } | |
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 | 161 |
214 /// Owned binary data. | 162 /// Owned binary data. |
215 /// | 163 /// |
216 /// For borrowed data, see [`BorrowedBinaryData`]. | 164 /// You can take ownership of the stored data by destructuring it: |
217 /// You can take ownership of the stored data with `.into::<Vec<u8>>()`. | 165 /// |
166 /// ``` | |
167 /// # use nonstick::BinaryData; | |
168 /// # let binary_data = BinaryData::new(vec![99, 88, 77], 66); | |
169 /// let (data, data_type) = binary_data.into(); | |
170 /// ``` | |
218 #[derive(Debug, Default, PartialEq)] | 171 #[derive(Debug, Default, PartialEq)] |
219 pub struct BinaryData { | 172 pub struct BinaryData { |
220 data: Vec<u8>, | 173 data: Vec<u8>, |
221 data_type: u8, | 174 data_type: u8, |
222 } | 175 } |
223 | 176 |
224 impl BinaryData { | 177 impl BinaryData { |
225 /// Creates a `BinaryData` with the given contents and type. | 178 /// Creates a `BinaryData` with the given contents and type. |
226 pub fn new(data: Vec<u8>, data_type: u8) -> Self { | 179 pub fn new(data: impl Into<Vec<u8>>, data_type: u8) -> Self { |
227 Self { data, data_type } | 180 Self { data: data.into(), data_type } |
228 } | 181 } |
229 /// A borrowed view of the data here. | 182 } |
230 pub fn data(&self) -> &[u8] { | 183 |
231 &self.data | 184 impl From<BinaryData> for (Vec<u8>, u8) { |
232 } | 185 fn from(value: BinaryData) -> Self { |
233 /// The type of the data stored in this. | 186 (value.data, value.data_type) |
234 pub fn data_type(&self) -> u8 { | 187 } |
235 self.data_type | 188 } |
236 } | 189 |
237 } | 190 impl From<(&'_[u8], u8)> for BinaryData { |
238 | 191 fn from((data, data_type): (&'_[u8], u8)) -> Self { |
239 impl<'a> From<BorrowedBinaryData<'a>> for BinaryData { | |
240 fn from(value: BorrowedBinaryData) -> Self { | |
241 Self { | 192 Self { |
242 data: value.data.to_vec(), | 193 data: data.to_vec(), |
243 data_type: value.data_type, | 194 data_type, |
244 } | 195 } |
196 } | |
197 } | |
198 | |
199 impl<'a> From<&'a BinaryData> for (&'a[u8], u8) { | |
200 fn from(value: &'a BinaryData) -> Self { | |
201 (&value.data, value.data_type) | |
245 } | 202 } |
246 } | 203 } |
247 | 204 |
248 impl From<BinaryData> for Vec<u8> { | 205 impl From<BinaryData> for Vec<u8> { |
249 /// Takes ownership of the data stored herein. | 206 /// Takes ownership of the data stored herein. |
259 "" | 216 "" |
260 "While this does not have an answer, [`Conversation`] implementations" | 217 "While this does not have an answer, [`Conversation`] implementations" |
261 "should still call [`set_answer`][`QAndA::set_answer`] to verify that" | 218 "should still call [`set_answer`][`QAndA::set_answer`] to verify that" |
262 "the message has been displayed (or actively discarded)." | 219 "the message has been displayed (or actively discarded)." |
263 ); | 220 ); |
264 impl<'a> InfoMsg<'a> { | |
265 /// Creates an informational message to send to the user. | |
266 pub fn new(message: &'a str) -> Self { | |
267 Self { | |
268 q: message, | |
269 a: Cell::new(Err(ErrorCode::ConversationError)), | |
270 } | |
271 } | |
272 } | |
273 | 221 |
274 q_and_a!( | 222 q_and_a!( |
275 ErrorMsg<'a, Q = &'a str, A = ()>, | 223 ErrorMsg<'a, Q = &'a str, A = ()>, |
276 Message::Error, | 224 Message::Error, |
277 "An error message to be passed to the user." | 225 "An error message to be passed to the user." |
278 "" | 226 "" |
279 "While this does not have an answer, [`Conversation`] implementations" | 227 "While this does not have an answer, [`Conversation`] implementations" |
280 "should still call [`set_answer`][`QAndA::set_answer`] to verify that" | 228 "should still call [`set_answer`][`QAndA::set_answer`] to verify that" |
281 "the message has been displayed (or actively discarded)." | 229 "the message has been displayed (or actively discarded)." |
282 | |
283 ); | 230 ); |
284 impl<'a> ErrorMsg<'a> { | |
285 /// Creates an error message to send to the user. | |
286 pub fn new(message: &'a str) -> Self { | |
287 Self { | |
288 q: message, | |
289 a: Cell::new(Err(ErrorCode::ConversationError)), | |
290 } | |
291 } | |
292 } | |
293 | 231 |
294 /// A channel for PAM modules to request information from the user. | 232 /// A channel for PAM modules to request information from the user. |
295 /// | 233 /// |
296 /// This trait is used by both applications and PAM modules: | 234 /// This trait is used by both applications and PAM modules: |
297 /// | 235 /// |
313 /// | 251 /// |
314 /// This can be used to wrap a free-floating function for use as a | 252 /// This can be used to wrap a free-floating function for use as a |
315 /// Conversation: | 253 /// Conversation: |
316 /// | 254 /// |
317 /// ``` | 255 /// ``` |
318 /// use nonstick::conv::{Conversation, Message, conversation_func}; | 256 /// use nonstick::conv::{conversation_func, Conversation, Message}; |
319 /// mod some_library { | 257 /// mod some_library { |
320 /// # use nonstick::Conversation; | 258 /// # use nonstick::Conversation; |
321 /// pub fn get_auth_data(conv: &mut impl Conversation) { /* ... */ } | 259 /// pub fn get_auth_data(conv: &mut impl Conversation) { |
260 /// /* ... */ | |
261 /// } | |
322 /// } | 262 /// } |
323 /// | 263 /// |
324 /// fn my_terminal_prompt(messages: &[Message]) { | 264 /// fn my_terminal_prompt(messages: &[Message]) { |
325 /// // ... | 265 /// // ... |
326 /// # todo!() | 266 /// # todo!() |
362 /// ``` | 302 /// ``` |
363 /// | 303 /// |
364 /// or to use a `SimpleConversation` as a `Conversation`: | 304 /// or to use a `SimpleConversation` as a `Conversation`: |
365 /// | 305 /// |
366 /// ``` | 306 /// ``` |
307 /// use nonstick::{Conversation, SimpleConversation}; | |
367 /// use secure_string::SecureString; | 308 /// use secure_string::SecureString; |
368 /// use nonstick::{Conversation, SimpleConversation}; | |
369 /// # use nonstick::{BinaryData, Result}; | 309 /// # use nonstick::{BinaryData, Result}; |
370 /// mod some_library { | 310 /// mod some_library { |
371 /// # use nonstick::Conversation; | 311 /// # use nonstick::Conversation; |
372 /// pub fn get_auth_data(conv: &mut impl Conversation) { /* ... */ } | 312 /// pub fn get_auth_data(conv: &mut impl Conversation) { /* ... */ |
373 /// } | 313 /// } |
374 /// | 314 /// } |
375 /// struct MySimpleConvo { /* ... */ } | 315 /// |
316 /// struct MySimpleConvo {/* ... */} | |
376 /// # impl MySimpleConvo { fn new() -> Self { Self{} } } | 317 /// # impl MySimpleConvo { fn new() -> Self { Self{} } } |
377 /// | 318 /// |
378 /// impl SimpleConversation for MySimpleConvo { | 319 /// impl SimpleConversation for MySimpleConvo { |
379 /// // ... | 320 /// // ... |
380 /// # fn prompt(&mut self, request: &str) -> Result<String> { | 321 /// # fn prompt(&mut self, request: &str) -> Result<String> { |
395 /// # | 336 /// # |
396 /// # fn info_msg(&mut self, message: &str) { | 337 /// # fn info_msg(&mut self, message: &str) { |
397 /// # todo!() | 338 /// # todo!() |
398 /// # } | 339 /// # } |
399 /// # | 340 /// # |
400 /// # fn binary_prompt(&mut self, data: &[u8], data_type: u8) -> Result<BinaryData> { | 341 /// # fn binary_prompt(&mut self, (data, data_type): (&[u8], u8)) -> Result<BinaryData> { |
401 /// # todo!() | 342 /// # todo!() |
402 /// # } | 343 /// # } |
403 /// } | 344 /// } |
404 /// | 345 /// |
405 /// fn main() { | 346 /// fn main() { |
411 /// Lets you use this simple conversation as a full [Conversation]. | 352 /// Lets you use this simple conversation as a full [Conversation]. |
412 /// | 353 /// |
413 /// The wrapper takes each message received in [`Conversation::communicate`] | 354 /// The wrapper takes each message received in [`Conversation::communicate`] |
414 /// and passes them one-by-one to the appropriate method, | 355 /// and passes them one-by-one to the appropriate method, |
415 /// then collects responses to return. | 356 /// then collects responses to return. |
416 fn as_conversation(&mut self) -> Demux<Self> | 357 fn as_conversation(&mut self) -> Demux<'_, Self> |
417 where | 358 where |
418 Self: Sized, | 359 Self: Sized, |
419 { | 360 { |
420 Demux(self) | 361 Demux(self) |
421 } | 362 } |
430 /// Alerts the user to an error. | 371 /// Alerts the user to an error. |
431 fn error_msg(&mut self, message: &str); | 372 fn error_msg(&mut self, message: &str); |
432 /// Sends an informational message to the user. | 373 /// Sends an informational message to the user. |
433 fn info_msg(&mut self, message: &str); | 374 fn info_msg(&mut self, message: &str); |
434 /// Requests binary data from the user (a Linux-PAM extension). | 375 /// Requests binary data from the user (a Linux-PAM extension). |
435 fn binary_prompt(&mut self, data: &[u8], data_type: u8) -> Result<BinaryData>; | 376 fn binary_prompt(&mut self, data_and_type: (&[u8], u8)) -> Result<BinaryData>; |
436 } | 377 } |
437 | 378 |
438 macro_rules! conv_fn { | 379 macro_rules! conv_fn { |
439 ($fn_name:ident($($param:ident: $pt:ty),+) -> $resp_type:ty { $msg:ty }) => { | 380 ($fn_name:ident($($param:tt: $pt:ty),+) -> $resp_type:ty { $msg:ty }) => { |
440 fn $fn_name(&mut self, $($param: $pt),*) -> Result<$resp_type> { | 381 fn $fn_name(&mut self, $($param: $pt),*) -> Result<$resp_type> { |
441 let prompt = <$msg>::new($($param),*); | 382 let prompt = <$msg>::new($($param),*); |
442 self.communicate(&[prompt.message()]); | 383 self.communicate(&[prompt.message()]); |
443 prompt.answer() | 384 prompt.answer() |
444 } | 385 } |
445 }; | 386 }; |
446 ($fn_name:ident($($param:ident: $pt:ty),+) { $msg:ty }) => { | 387 ($fn_name:ident($($param:tt: $pt:ty),+) { $msg:ty }) => { |
447 fn $fn_name(&mut self, $($param: $pt),*) { | 388 fn $fn_name(&mut self, $($param: $pt),*) { |
448 self.communicate(&[<$msg>::new($($param),*).message()]); | 389 self.communicate(&[<$msg>::new($($param),*).message()]); |
449 } | 390 } |
450 }; | 391 }; |
451 } | 392 } |
454 conv_fn!(prompt(message: &str) -> String { QAndA }); | 395 conv_fn!(prompt(message: &str) -> String { QAndA }); |
455 conv_fn!(masked_prompt(message: &str) -> SecureString { MaskedQAndA } ); | 396 conv_fn!(masked_prompt(message: &str) -> SecureString { MaskedQAndA } ); |
456 conv_fn!(radio_prompt(message: &str) -> String { RadioQAndA }); | 397 conv_fn!(radio_prompt(message: &str) -> String { RadioQAndA }); |
457 conv_fn!(error_msg(message: &str) { ErrorMsg }); | 398 conv_fn!(error_msg(message: &str) { ErrorMsg }); |
458 conv_fn!(info_msg(message: &str) { InfoMsg }); | 399 conv_fn!(info_msg(message: &str) { InfoMsg }); |
459 conv_fn!(binary_prompt(data: &[u8], data_type: u8) -> BinaryData { BinaryQAndA }); | 400 conv_fn!(binary_prompt((data, data_type): (&[u8], u8)) -> BinaryData { BinaryQAndA }); |
460 } | 401 } |
461 | 402 |
462 /// A [`Conversation`] which asks the questions one at a time. | 403 /// A [`Conversation`] which asks the questions one at a time. |
463 /// | 404 /// |
464 /// This is automatically created by [`SimpleConversation::as_conversation`]. | 405 /// This is automatically created by [`SimpleConversation::as_conversation`]. |
483 self.0.error_msg(prompt.question()); | 424 self.0.error_msg(prompt.question()); |
484 prompt.set_answer(Ok(())) | 425 prompt.set_answer(Ok(())) |
485 } | 426 } |
486 Message::BinaryPrompt(prompt) => { | 427 Message::BinaryPrompt(prompt) => { |
487 let q = prompt.question(); | 428 let q = prompt.question(); |
488 prompt.set_answer(self.0.binary_prompt(q.data, q.data_type)) | 429 prompt.set_answer(self.0.binary_prompt(q)) |
489 } | 430 } |
490 } | 431 } |
491 } | 432 } |
492 } | 433 } |
493 } | 434 } |
494 | 435 |
495 #[cfg(test)] | 436 #[cfg(test)] |
496 mod tests { | 437 mod tests { |
497 use super::{ | 438 use super::{ |
498 BinaryQAndA, Conversation, ErrorMsg, InfoMsg, MaskedQAndA, Message, QAndA, | 439 BinaryQAndA, Conversation, ErrorMsg, InfoMsg, MaskedQAndA, Message, QAndA, RadioQAndA, |
499 RadioQAndA, Result, SecureString, SimpleConversation, | 440 Result, SecureString, SimpleConversation, |
500 }; | 441 }; |
501 use crate::constants::ErrorCode; | 442 use crate::constants::ErrorCode; |
502 use crate::BinaryData; | 443 use crate::BinaryData; |
503 | 444 |
504 #[test] | 445 #[test] |
531 } | 472 } |
532 fn info_msg(&mut self, message: &str) { | 473 fn info_msg(&mut self, message: &str) { |
533 self.info_ran = true; | 474 self.info_ran = true; |
534 assert_eq!("did you know", message); | 475 assert_eq!("did you know", message); |
535 } | 476 } |
536 fn binary_prompt(&mut self, data: &[u8], data_type: u8) -> Result<BinaryData> { | 477 fn binary_prompt(&mut self, data_and_type: (&[u8], u8)) -> Result<BinaryData> { |
537 assert_eq!(&[10, 9, 8], data); | 478 assert_eq!((&[10, 9, 8][..], 66), data_and_type); |
538 assert_eq!(66, data_type); | |
539 Ok(BinaryData::new(vec![5, 5, 5], 5)) | 479 Ok(BinaryData::new(vec![5, 5, 5], 5)) |
540 } | 480 } |
541 } | 481 } |
542 | 482 |
543 let mut tester = DemuxTester::default(); | 483 let mut tester = DemuxTester::default(); |
571 // Test the Linux extensions separately. | 511 // Test the Linux extensions separately. |
572 | 512 |
573 let mut conv = tester.as_conversation(); | 513 let mut conv = tester.as_conversation(); |
574 | 514 |
575 let radio = RadioQAndA::new("channel?"); | 515 let radio = RadioQAndA::new("channel?"); |
576 let bin = BinaryQAndA::new(&[10, 9, 8], 66); | 516 let bin = BinaryQAndA::new((&[10, 9, 8], 66)); |
577 conv.communicate(&[radio.message(), bin.message()]); | 517 conv.communicate(&[radio.message(), bin.message()]); |
578 | 518 |
579 assert_eq!("zero", radio.answer().unwrap()); | 519 assert_eq!("zero", radio.answer().unwrap()); |
580 assert_eq!(BinaryData::new(vec![5, 5, 5], 5), bin.answer().unwrap()); | 520 assert_eq!(BinaryData::new(vec![5, 5, 5], 5), bin.answer().unwrap()); |
581 } | 521 } |
603 Message::MaskedPrompt(ask) => { | 543 Message::MaskedPrompt(ask) => { |
604 assert_eq!("password!", ask.question()); | 544 assert_eq!("password!", ask.question()); |
605 ask.set_answer(Ok("open sesame".into())) | 545 ask.set_answer(Ok("open sesame".into())) |
606 } | 546 } |
607 Message::BinaryPrompt(prompt) => { | 547 Message::BinaryPrompt(prompt) => { |
608 assert_eq!(&[1, 2, 3], prompt.question().data); | 548 assert_eq!((&[1, 2, 3][..], 69), prompt.question()); |
609 assert_eq!(69, prompt.question().data_type); | |
610 prompt.set_answer(Ok(BinaryData::new(vec![3, 2, 1], 42))) | 549 prompt.set_answer(Ok(BinaryData::new(vec![3, 2, 1], 42))) |
611 } | 550 } |
612 Message::RadioPrompt(ask) => { | 551 Message::RadioPrompt(ask) => { |
613 assert_eq!("radio?", ask.question()); | 552 assert_eq!("radio?", ask.question()); |
614 ask.set_answer(Ok("yes".to_owned())) | 553 ask.set_answer(Ok("yes".to_owned())) |
634 tester.info_msg("let me tell you"); | 573 tester.info_msg("let me tell you"); |
635 { | 574 { |
636 assert_eq!("yes", tester.radio_prompt("radio?").unwrap()); | 575 assert_eq!("yes", tester.radio_prompt("radio?").unwrap()); |
637 assert_eq!( | 576 assert_eq!( |
638 BinaryData::new(vec![3, 2, 1], 42), | 577 BinaryData::new(vec![3, 2, 1], 42), |
639 tester.binary_prompt(&[1, 2, 3], 69).unwrap(), | 578 tester.binary_prompt((&[1, 2, 3], 69)).unwrap(), |
640 ) | 579 ) |
641 } | 580 } |
642 assert_eq!( | 581 assert_eq!( |
643 ErrorCode::BufferError, | 582 ErrorCode::BufferError, |
644 tester.prompt("should_error").unwrap_err(), | 583 tester.prompt("should_error").unwrap_err(), |