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