Mercurial > crates > nonstick
diff src/conv.rs @ 130:80c07e5ab22f
Transfer over (almost) completely to using libpam-sys.
This reimplements everything in nonstick on top of the new -sys crate.
We don't yet use libpam-sys's helpers for binary message payloads. Soon.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Tue, 01 Jul 2025 06:11:43 -0400 |
parents | f3e260f9ddcb |
children |
line wrap: on
line diff
--- a/src/conv.rs Mon Jun 30 23:49:54 2025 -0400 +++ b/src/conv.rs Tue Jul 01 06:11:43 2025 -0400 @@ -9,12 +9,10 @@ use std::fmt::Debug; use std::result::Result as StdResult; -/// The types of message and request that can be sent to a user. -/// -/// The data within each enum value is the prompt (or other information) -/// that will be presented to the user. +/// An individual pair of request/response to be sent to the user. +#[derive(Debug)] #[non_exhaustive] -pub enum Message<'a> { +pub enum Exchange<'a> { Prompt(&'a QAndA<'a>), MaskedPrompt(&'a MaskedQAndA<'a>), Error(&'a ErrorMsg<'a>), @@ -23,22 +21,22 @@ BinaryPrompt(&'a BinaryQAndA<'a>), } -impl Message<'_> { +impl Exchange<'_> { /// Sets an error answer on this question, without having to inspect it. /// /// Use this as a default match case: /// /// ``` - /// use nonstick::conv::{Message, QAndA}; + /// use nonstick::conv::{Exchange, QAndA}; /// use nonstick::ErrorCode; /// - /// fn cant_respond(message: Message) { + /// fn cant_respond(message: Exchange) { /// match message { - /// Message::Info(i) => { + /// Exchange::Info(i) => { /// eprintln!("fyi, {}", i.question()); /// i.set_answer(Ok(())) /// } - /// Message::Error(e) => { + /// Exchange::Error(e) => { /// eprintln!("ERROR: {}", e.question()); /// e.set_answer(Ok(())) /// } @@ -48,12 +46,12 @@ /// } pub fn set_error(&self, err: ErrorCode) { match *self { - Message::Prompt(m) => m.set_answer(Err(err)), - Message::MaskedPrompt(m) => m.set_answer(Err(err)), - Message::Error(m) => m.set_answer(Err(err)), - Message::Info(m) => m.set_answer(Err(err)), - Message::RadioPrompt(m) => m.set_answer(Err(err)), - Message::BinaryPrompt(m) => m.set_answer(Err(err)), + Exchange::Prompt(m) => m.set_answer(Err(err)), + Exchange::MaskedPrompt(m) => m.set_answer(Err(err)), + Exchange::Error(m) => m.set_answer(Err(err)), + Exchange::Info(m) => m.set_answer(Err(err)), + Exchange::RadioPrompt(m) => m.set_answer(Err(err)), + Exchange::BinaryPrompt(m) => m.set_answer(Err(err)), } } } @@ -76,8 +74,8 @@ } } - /// Converts this Q&A into a [`Message`] for the [`Conversation`]. - pub fn message(&self) -> Message<'_> { + /// Converts this Q&A into a [`Exchange`] for the [`Conversation`]. + pub fn exchange(&self) -> Exchange<'_> { $val(self) } @@ -110,9 +108,7 @@ $(#[$m])* impl fmt::Debug for $name<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> StdResult<(), fmt::Error> { - #[derive(Debug)] - struct $name<'a> { q: $qt } - fmt::Debug::fmt(&$name { q: self.q }, f) + f.debug_struct(stringify!($name)).field("q", &self.q).finish_non_exhaustive() } } }; @@ -123,7 +119,7 @@ /// /// In other words, a password entry prompt. MaskedQAndA<'a, Q=&'a str, A=String>, - Message::MaskedPrompt + Exchange::MaskedPrompt ); q_and_a!( @@ -133,7 +129,7 @@ /// When the user types, their input will be shown to them. /// It can be used for things like usernames. QAndA<'a, Q=&'a str, A=String>, - Message::Prompt + Exchange::Prompt ); q_and_a!( @@ -143,7 +139,7 @@ /// questions, but nowhere in the documentation is it specified /// what the format of the answer will be, or how this should be shown. RadioQAndA<'a, Q=&'a str, A=String>, - Message::RadioPrompt + Exchange::RadioPrompt ); q_and_a!( @@ -156,7 +152,7 @@ /// The `data_type` tag is a value that is simply passed through /// to the application. PAM does not define any meaning for it. BinaryQAndA<'a, Q=(&'a [u8], u8), A=BinaryData>, - Message::BinaryPrompt + Exchange::BinaryPrompt ); /// Owned binary data. @@ -208,7 +204,7 @@ /// should still call [`set_answer`][`QAndA::set_answer`] to verify that /// the message has been displayed (or actively discarded). InfoMsg<'a, Q = &'a str, A = ()>, - Message::Info + Exchange::Info ); q_and_a!( @@ -218,7 +214,7 @@ /// should still call [`set_answer`][`QAndA::set_answer`] to verify that /// the message has been displayed (or actively discarded). ErrorMsg<'a, Q = &'a str, A = ()>, - Message::Error + Exchange::Error ); /// A channel for PAM modules to request information from the user. @@ -236,7 +232,7 @@ /// as there were messages in the request; one corresponding to each. /// /// TODO: write detailed documentation about how to use this. - fn communicate(&self, messages: &[Message]); + fn communicate(&self, messages: &[Exchange]); } /// Turns a simple function into a [`Conversation`]. @@ -245,7 +241,7 @@ /// Conversation: /// /// ``` -/// use nonstick::conv::{conversation_func, Conversation, Message}; +/// use nonstick::conv::{conversation_func, Conversation, Exchange}; /// mod some_library { /// # use nonstick::Conversation; /// pub fn get_auth_data(conv: &mut impl Conversation) { @@ -253,7 +249,7 @@ /// } /// } /// -/// fn my_terminal_prompt(messages: &[Message]) { +/// fn my_terminal_prompt(messages: &[Exchange]) { /// // ... /// # unimplemented!() /// } @@ -262,14 +258,14 @@ /// some_library::get_auth_data(&mut conversation_func(my_terminal_prompt)); /// } /// ``` -pub fn conversation_func(func: impl Fn(&[Message])) -> impl Conversation { +pub fn conversation_func(func: impl Fn(&[Exchange])) -> impl Conversation { FunctionConvo(func) } -struct FunctionConvo<C: Fn(&[Message])>(C); +struct FunctionConvo<C: Fn(&[Exchange])>(C); -impl<C: Fn(&[Message])> Conversation for FunctionConvo<C> { - fn communicate(&self, messages: &[Message]) { +impl<C: Fn(&[Exchange])> Conversation for FunctionConvo<C> { + fn communicate(&self, messages: &[Exchange]) { self.0(messages) } } @@ -399,14 +395,14 @@ $(#[$m])* fn $fn_name(&self, $($param: $pt),*) -> Result<$resp_type> { let prompt = <$msg>::new($($param),*); - self.communicate(&[prompt.message()]); + self.communicate(&[prompt.exchange()]); prompt.answer() } }; ($(#[$m:meta])*$fn_name:ident($($param:tt: $pt:ty),+) { $msg:ty }) => { $(#[$m])* fn $fn_name(&self, $($param: $pt),*) { - self.communicate(&[<$msg>::new($($param),*).message()]); + self.communicate(&[<$msg>::new($($param),*).exchange()]); } }; } @@ -433,25 +429,25 @@ } impl<CA: ConversationAdapter> Conversation for Demux<CA> { - fn communicate(&self, messages: &[Message]) { + fn communicate(&self, messages: &[Exchange]) { for msg in messages { match msg { - Message::Prompt(prompt) => prompt.set_answer(self.0.prompt(prompt.question())), - Message::MaskedPrompt(prompt) => { + Exchange::Prompt(prompt) => prompt.set_answer(self.0.prompt(prompt.question())), + Exchange::MaskedPrompt(prompt) => { prompt.set_answer(self.0.masked_prompt(prompt.question())) } - Message::RadioPrompt(prompt) => { + Exchange::RadioPrompt(prompt) => { prompt.set_answer(self.0.radio_prompt(prompt.question())) } - Message::Info(prompt) => { + Exchange::Info(prompt) => { self.0.info_msg(prompt.question()); prompt.set_answer(Ok(())) } - Message::Error(prompt) => { + Exchange::Error(prompt) => { self.0.error_msg(prompt.question()); prompt.set_answer(Ok(())) } - Message::BinaryPrompt(prompt) => { + Exchange::BinaryPrompt(prompt) => { let q = prompt.question(); prompt.set_answer(self.0.binary_prompt(q)) } @@ -516,11 +512,11 @@ // Basic tests. conv.communicate(&[ - what.message(), - pass.message(), - err.message(), - info.message(), - has_err.message(), + what.exchange(), + pass.exchange(), + err.exchange(), + info.exchange(), + has_err.exchange(), ]); assert_eq!("whatwhat", what.answer().unwrap()); @@ -538,7 +534,7 @@ let radio = RadioQAndA::new("channel?"); let bin = BinaryQAndA::new((&[10, 9, 8], 66)); - conv.communicate(&[radio.message(), bin.message()]); + conv.communicate(&[radio.exchange(), bin.exchange()]); assert_eq!("zero", radio.answer().unwrap()); assert_eq!(BinaryData::from(([5, 5, 5], 5)), bin.answer().unwrap()); @@ -549,31 +545,31 @@ struct MuxTester; impl Conversation for MuxTester { - fn communicate(&self, messages: &[Message]) { + fn communicate(&self, messages: &[Exchange]) { if let [msg] = messages { match *msg { - Message::Info(info) => { + Exchange::Info(info) => { assert_eq!("let me tell you", info.question()); info.set_answer(Ok(())) } - Message::Error(error) => { + Exchange::Error(error) => { assert_eq!("oh no", error.question()); error.set_answer(Ok(())) } - Message::Prompt(prompt) => prompt.set_answer(match prompt.question() { + Exchange::Prompt(prompt) => prompt.set_answer(match prompt.question() { "should_err" => Err(ErrorCode::PermissionDenied), "question" => Ok("answer".to_owned()), other => panic!("unexpected question {other:?}"), }), - Message::MaskedPrompt(ask) => { + Exchange::MaskedPrompt(ask) => { assert_eq!("password!", ask.question()); ask.set_answer(Ok("open sesame".into())) } - Message::BinaryPrompt(prompt) => { + Exchange::BinaryPrompt(prompt) => { assert_eq!((&[1, 2, 3][..], 69), prompt.question()); prompt.set_answer(Ok(BinaryData::from((&[3, 2, 1], 42)))) } - Message::RadioPrompt(ask) => { + Exchange::RadioPrompt(ask) => { assert_eq!("radio?", ask.question()); ask.set_answer(Ok("yes".to_owned())) }