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(), |
