1use core::fmt;2use std::borrow::Cow;3use std::future::Future;4use std::result;56pub mod dbus;7pub mod rofi;89#[derive(thiserror::Error, Debug)]10pub enum Error {11 #[error("user has cancelled input")]12 Cancel,13 #[error("input error: {0}")]14 InputError(String),15}1617pub type Result<T, E = Error> = result::Result<T, E>;1819#[cfg_attr(feature = "dbus", derive(zbus::zvariant::Type))]20#[derive(serde::Serialize, serde::Deserialize, Clone)]21pub struct Source(Cow<'static, str>);22impl fmt::Display for Source {23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {24 write!(f, "<u>{}</u>", self.0)25 }26}2728pub trait Prompter {29 fn prompt_radio(30 &self,31 prompt: &str,32 description: &str,33 source: &[Source],34 ) -> impl Future<Output = Result<bool>> + Send {35 let fut = self.prompt_enum(prompt, description, &["No", "Yes"], source);36 async { fut.await.map(|v| v == 1) }37 }38 fn prompt_enum(39 &self,40 prompt: &str,41 description: &str,42 variants: &[&str],43 source: &[Source],44 ) -> impl Future<Output = Result<u32>> + Send;45 fn prompt_text(46 &self,47 echo: bool,48 prompt: &str,49 description: &str,50 source: &[Source],51 ) -> impl Future<Output = Result<String>> + Send;52 fn display_text(53 &self,54 error: bool,55 description: &str,56 source: &[Source],57 ) -> impl Future<Output = Result<()>> + Send;58}59pub trait BlockingPrompter {60 fn prompt_radio(&self, prompt: &str, description: &str, source: &[Source]) -> Result<bool> {61 self.prompt_enum(prompt, description, &["No", "Yes"], source)62 .map(|v| v == 1)63 }64 fn prompt_enum(65 &self,66 prompt: &str,67 description: &str,68 variants: &[&str],69 source: &[Source],70 ) -> Result<u32>;71 fn prompt_text(72 &self,73 echo: bool,74 prompt: &str,75 description: &str,76 source: &[Source],77 ) -> Result<String>;78 fn display_text(&self, error: bool, description: &str, source: &[Source]) -> Result<()>;79}8081pub struct PrependSourcePrompter<P> {82 prompter: P,83 source: Vec<Source>,84}85impl<P> PrependSourcePrompter<P> {86 fn source(&self, input: &[Source]) -> Vec<Source> {87 let mut out = self.source.clone();88 out.extend(input.iter().cloned());89 out90 }91}92impl<P> Prompter for PrependSourcePrompter<P>93where94 P: Prompter + Sync,95{96 async fn prompt_enum(97 &self,98 prompt: &str,99 description: &str,100 variants: &[&str],101 source: &[Source],102 ) -> Result<u32> {103 self.prompter104 .prompt_enum(prompt, description, variants, &self.source(source))105 .await106 }107108 async fn prompt_text(109 &self,110 echo: bool,111 prompt: &str,112 description: &str,113 source: &[Source],114 ) -> Result<String> {115 self.prompter116 .prompt_text(echo, prompt, description, &self.source(source))117 .await118 }119120 async fn display_text(&self, error: bool, description: &str, source: &[Source]) -> Result<()> {121 self.prompter122 .display_text(error, description, &self.source(source))123 .await124 }125}