git.delta.rocks / remowt / refs/commits / 6bfa3e083e52

difftreelog

feat(ui-prompt) declarative bifrost endpoint

nswuwzxtYaroslav Bolyukin2026-01-25parent: #06498c3.patch.diff
in: trunk

5 files changed

modifiedcrates/ui-prompt/Cargo.tomldiffbeforeafterboth
--- a/crates/ui-prompt/Cargo.toml
+++ b/crates/ui-prompt/Cargo.toml
@@ -5,11 +5,13 @@
 
 [dependencies]
 bifrostlink.workspace = true
-serde = "1.0.204"
+bifrostlink-macros.workspace = true
+serde.workspace = true
+serde_json.workspace = true
 thiserror = "1.0.63"
-tokio = { version = "1.39.2", features = ["io-util", "macros", "process", "rt"] }
-tracing = "0.1.40"
-zbus = { version = "4.4.0", optional = true }
+tokio = { workspace = true, features = ["io-util", "macros", "process", "rt"] }
+tracing.workspace = true
+zbus = { workspace = true, optional = true }
 
 [features]
 default = ["dbus"]
modifiedcrates/ui-prompt/src/bifrost.rsdiffbeforeafterboth
--- a/crates/ui-prompt/src/bifrost.rs
+++ b/crates/ui-prompt/src/bifrost.rs
@@ -1,72 +1,73 @@
-use bifrostlink::error::ErrorT;
-use bifrostlink::{request, AddressT, Rpc};
+use bifrostlink::{Config, Rpc};
+use bifrostlink_macros::endpoints;
 use serde::{Deserialize, Serialize};
 
 use crate::{Error, Prompter, Source};
 
-pub struct BifrostPrompter<A: AddressT, E: ErrorT> {
-	pub address: A,
-	pub rpc: Rpc<A, E>,
-}
+pub struct PromptEndpoints<P>(pub P);
 
-#[derive(Serialize, Deserialize)]
-struct EnumRequest {
-	prompt: String,
-	description: String,
-	variants: Vec<String>,
-	source: Vec<Source>,
-}
-#[derive(Serialize, Deserialize)]
-struct EnumResponse {
-	value: u32,
-}
-request!(EnumRequest => EnumResponse);
+#[endpoints(ns = 2)]
+impl<P> PromptEndpoints<P>
+where
+	P: Prompter + Send + Sync + 'static,
+{
+	#[endpoints(id = 1, cancel)]
+	async fn prompt_enum(
+		&self,
+		prompt: String,
+		description: String,
+		variants: Vec<String>,
+		source: Vec<Source>,
+	) -> Result<u32, Error> {
+		let variants: Vec<&str> = variants.iter().map(|v| v.as_str()).collect();
+		self.0
+			.prompt_enum(&prompt, &description, &variants, &source)
+			.await
+	}
 
-#[derive(Serialize, Deserialize)]
-struct TextRequest {
-	echo: bool,
-	prompt: String,
-	description: String,
-	source: Vec<Source>,
-}
-#[derive(Serialize, Deserialize)]
-struct TextResponse {
-	value: String,
-}
-request!(TextRequest => TextResponse);
+	#[endpoints(id = 2, cancel)]
+	async fn prompt_text(
+		&self,
+		echo: bool,
+		prompt: String,
+		description: String,
+		source: Vec<Source>,
+	) -> Result<String, Error> {
+		self.0
+			.prompt_text(echo, &prompt, &description, &source)
+			.await
+	}
 
-#[derive(Serialize, Deserialize)]
-struct DisplayRequest {
-	error: bool,
-	description: String,
-	source: Vec<Source>,
+	#[endpoints(id = 3, cancel)]
+	async fn display_text(
+		&self,
+		error: bool,
+		description: String,
+		source: Vec<Source>,
+	) -> Result<(), Error> {
+		self.0.display_text(error, &description, &source).await
+	}
 }
-request!(DisplayRequest => ());
 
-impl<A: AddressT, E: ErrorT> Prompter for BifrostPrompter<A, E>
+impl<C: Config> Prompter for PromptEndpointsClient<C>
 where
-	crate::Error: From<E>,
+	Error: ToString,
 {
 	async fn prompt_enum(
 		&self,
 		prompt: &str,
 		description: &str,
 		variants: &[&str],
-		source: &[crate::Source],
+		source: &[Source],
 	) -> crate::Result<u32> {
-		let res = self
-			.rpc
-			.request(
-				self.address.clone(),
-				&EnumRequest {
-					prompt: prompt.to_owned(),
-					description: description.to_owned(),
-					variants: variants.into_iter().map(|v| (*v).to_owned()).collect(),
-					source: source.to_vec(),
-				},
-			)
-			.await?;
-		Ok(res.value)
+		self.prompt_enum(
+			prompt.to_owned(),
+			description.to_owned(),
+			variants.iter().map(|v| (*v).to_owned()).collect(),
+			source.to_vec(),
+		)
+		.await
+		.map_err(|e| Error::Remote(e.to_string()))?
 	}
 
 	async fn prompt_text(
@@ -74,93 +75,35 @@
 		echo: bool,
 		prompt: &str,
 		description: &str,
-		source: &[crate::Source],
+		source: &[Source],
 	) -> crate::Result<String> {
-		let res = self
-			.rpc
-			.request(
-				self.address.clone(),
-				&TextRequest {
-					echo,
-					prompt: prompt.to_owned(),
-					description: description.to_owned(),
-					source: source.to_vec(),
-				},
-			)
-			.await?;
-		Ok(res.value)
+		self.prompt_text(
+			echo,
+			prompt.to_owned(),
+			description.to_owned(),
+			source.to_vec(),
+		)
+		.await
+		.map_err(|e| Error::Remote(e.to_string()))?
 	}
 
 	async fn display_text(
 		&self,
 		error: bool,
 		description: &str,
-		source: &[crate::Source],
+		source: &[Source],
 	) -> crate::Result<()> {
-		self.rpc
-			.request(
-				self.address.clone(),
-				&DisplayRequest {
-					error,
-					description: description.to_owned(),
-					source: source.to_vec(),
-				},
-			)
-			.await?;
-		Ok(())
+		self.display_text(error, description.to_owned(), source.to_vec())
+			.await
+			.map_err(|e| Error::Remote(e.to_string()))?
 	}
 }
 
-pub fn handle_bifrost_prompts<
-	P: Prompter + Clone + 'static,
-	A: AddressT,
-	E: ErrorT + From<Error>,
->(
-	rpc: &Rpc<A, E>,
-	prompt: P,
-) {
-	rpc.register_request_handler(true, {
-		let prompt = prompt.clone();
-		move |_addr, req: EnumRequest| {
-			let prompt = prompt.clone();
-			async move {
-				let i = prompt
-					.prompt_enum(
-						&req.prompt,
-						&req.description,
-						&req.variants.iter().map(|v| v.as_str()).collect::<Vec<_>>(),
-						&req.source,
-					)
-					.await?;
-
-				Ok(EnumResponse { value: i })
-			}
-		}
-	});
-	rpc.register_request_handler(true, {
-		let prompt = prompt.clone();
-		move |_addr, req: TextRequest| {
-			let prompt = prompt.clone();
-			async move {
-				let i = prompt
-					.prompt_text(req.echo, &req.prompt, &req.description, &req.source)
-					.await?;
-
-				Ok(TextResponse { value: i })
-			}
-		}
-	});
-	rpc.register_request_handler(true, {
-		let prompt = prompt.clone();
-		move |_addr, req: DisplayRequest| {
-			let prompt = prompt.clone();
-			async move {
-				prompt
-					.display_text(req.error, &req.description, &req.source)
-					.await?;
-
-				Ok(())
-			}
-		}
-	});
+pub fn serve_prompts<P, C>(rpc: &mut Rpc<C>, prompt: P)
+where
+	P: Prompter + Send + Sync + 'static,
+	C: Config,
+	C::Error: From<Error>,
+{
+	PromptEndpoints(prompt).register_endpoints(rpc);
 }
modifiedcrates/ui-prompt/src/dbus.rsdiffbeforeafterboth
--- a/crates/ui-prompt/src/dbus.rs
+++ b/crates/ui-prompt/src/dbus.rs
@@ -6,128 +6,130 @@
 use crate::{Error, Prompter};
 
 pub struct DbusPrompterInterface<P>(pub P);
+
 #[interface(name = "lach.PolkitInputHandler")]
 impl<P: Prompter + Send + Sync + 'static> DbusPrompterInterface<P> {
-    async fn prompt_radio(
-        &self,
-        prompt: &str,
-        description: &str,
-        source: Vec<Source>,
-    ) -> fdo::Result<bool> {
-        Ok(self.0.prompt_radio(prompt, description, &source).await?)
-    }
-    async fn prompt_text(
-        &self,
-        echo: bool,
-        prompt: &str,
-        description: &str,
-        source: Vec<Source>,
-    ) -> fdo::Result<String> {
-        Ok(self
-            .0
-            .prompt_text(echo, prompt, description, &source)
-            .await?)
-    }
-    async fn display_text(
-        &self,
-        error: bool,
-        description: &str,
-        source: Vec<Source>,
-    ) -> fdo::Result<()> {
-        Ok(self.0.display_text(error, description, &source).await?)
-    }
+	async fn prompt_radio(
+		&self,
+		prompt: &str,
+		description: &str,
+		source: Vec<Source>,
+	) -> fdo::Result<bool> {
+		Ok(self.0.prompt_radio(prompt, description, &source).await?)
+	}
+	async fn prompt_text(
+		&self,
+		echo: bool,
+		prompt: &str,
+		description: &str,
+		source: Vec<Source>,
+	) -> fdo::Result<String> {
+		Ok(self
+			.0
+			.prompt_text(echo, prompt, description, &source)
+			.await?)
+	}
+	async fn display_text(
+		&self,
+		error: bool,
+		description: &str,
+		source: Vec<Source>,
+	) -> fdo::Result<()> {
+		Ok(self.0.display_text(error, description, &source).await?)
+	}
 }
 
 #[proxy(interface = "lach.PolkitInputHandler")]
-trait DbusPrompter {
-    async fn prompt_enum(
-        &self,
-        prompt: &str,
-        description: &str,
-        variants: &[&str],
-        source: &[Source],
-    ) -> fdo::Result<u32>;
-    async fn prompt_text(
-        &self,
-        echo: bool,
-        prompt: &str,
-        description: &str,
-        source: &[Source],
-    ) -> fdo::Result<String>;
-    async fn display_text(
-        &self,
-        error: bool,
-        description: &str,
-        source: &[Source],
-    ) -> fdo::Result<()>;
+pub trait DbusPrompter {
+	async fn prompt_enum(
+		&self,
+		prompt: &str,
+		description: &str,
+		variants: &[&str],
+		source: &[Source],
+	) -> fdo::Result<u32>;
+	async fn prompt_text(
+		&self,
+		echo: bool,
+		prompt: &str,
+		description: &str,
+		source: &[Source],
+	) -> fdo::Result<String>;
+	async fn display_text(
+		&self,
+		error: bool,
+		description: &str,
+		source: &[Source],
+	) -> fdo::Result<()>;
 }
 
 impl Prompter for DbusPrompterProxy<'_> {
-    async fn prompt_enum(
-        &self,
-        prompt: &str,
-        description: &str,
-        variants: &[&str],
-        source: &[Source],
-    ) -> Result<u32> {
-        Ok(self
-            .prompt_enum(prompt, description, variants, source)
-            .await?)
-    }
+	async fn prompt_enum(
+		&self,
+		prompt: &str,
+		description: &str,
+		variants: &[&str],
+		source: &[Source],
+	) -> Result<u32> {
+		Ok(self
+			.prompt_enum(prompt, description, variants, source)
+			.await?)
+	}
 
-    async fn prompt_text(
-        &self,
-        echo: bool,
-        prompt: &str,
-        description: &str,
-        source: &[Source],
-    ) -> Result<String> {
-        Ok(self.prompt_text(echo, prompt, description, source).await?)
-    }
+	async fn prompt_text(
+		&self,
+		echo: bool,
+		prompt: &str,
+		description: &str,
+		source: &[Source],
+	) -> Result<String> {
+		Ok(self.prompt_text(echo, prompt, description, source).await?)
+	}
 
-    async fn display_text(&self, error: bool, description: &str, source: &[Source]) -> Result<()> {
-        Ok(self.display_text(error, description, source).await?)
-    }
+	async fn display_text(&self, error: bool, description: &str, source: &[Source]) -> Result<()> {
+		Ok(self.display_text(error, description, source).await?)
+	}
 }
 impl BlockingPrompter for DbusPrompterProxyBlocking<'_> {
-    fn prompt_enum(
-        &self,
-        prompt: &str,
-        description: &str,
-        variants: &[&str],
-        source: &[Source],
-    ) -> Result<u32> {
-        Ok(self.prompt_enum(prompt, description, variants, source)?)
-    }
+	fn prompt_enum(
+		&self,
+		prompt: &str,
+		description: &str,
+		variants: &[&str],
+		source: &[Source],
+	) -> Result<u32> {
+		Ok(self.prompt_enum(prompt, description, variants, source)?)
+	}
 
-    fn prompt_text(
-        &self,
-        echo: bool,
-        prompt: &str,
-        description: &str,
-        source: &[Source],
-    ) -> Result<String> {
-        Ok(self.prompt_text(echo, prompt, description, source)?)
-    }
+	fn prompt_text(
+		&self,
+		echo: bool,
+		prompt: &str,
+		description: &str,
+		source: &[Source],
+	) -> Result<String> {
+		Ok(self.prompt_text(echo, prompt, description, source)?)
+	}
 
-    fn display_text(&self, error: bool, description: &str, source: &[Source]) -> Result<()> {
-        Ok(self.display_text(error, description, source)?)
-    }
+	fn display_text(&self, error: bool, description: &str, source: &[Source]) -> Result<()> {
+		Ok(self.display_text(error, description, source)?)
+	}
 }
 
 impl From<fdo::Error> for Error {
-    fn from(value: fdo::Error) -> Self {
-        if matches!(value, fdo::Error::NoReply(_)) {
-            return Self::Cancel;
-        }
-        Self::InputError(format!("{value}"))
-    }
+	fn from(value: fdo::Error) -> Self {
+		if matches!(value, fdo::Error::NoReply(_)) {
+			return Self::Cancel;
+		}
+		Self::InputError(format!("{value}"))
+	}
 }
 impl From<Error> for fdo::Error {
-    fn from(value: Error) -> Self {
-        match value {
-            Error::Cancel => fdo::Error::NoReply("input was cancelled".to_owned()),
-            Error::InputError(e) => fdo::Error::Failed(e),
-        }
-    }
+	fn from(value: Error) -> Self {
+		match value {
+			Error::Cancel => fdo::Error::NoReply("input was cancelled".to_owned()),
+			Error::Remote(e) => fdo::Error::NoReply(format!("remote error occured: {e}")),
+			Error::InputError(e) => fdo::Error::Failed(e),
+		}
+	}
 }
modifiedcrates/ui-prompt/src/lib.rsdiffbeforeafterboth
--- a/crates/ui-prompt/src/lib.rs
+++ b/crates/ui-prompt/src/lib.rs
@@ -3,16 +3,18 @@
 use std::future::Future;
 use std::result;
 
+pub mod bifrost;
 pub mod dbus;
 pub mod rofi;
-pub mod bifrost;
 
-#[derive(thiserror::Error, Debug)]
+#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)]
 pub enum Error {
-    #[error("user has cancelled input")]
-    Cancel,
-    #[error("input error: {0}")]
-    InputError(String),
+	#[error("user has cancelled input")]
+	Cancel,
+	#[error("input error: {0}")]
+	InputError(String),
+	#[error("unknown remote error: {0}")]
+	Remote(String),
 }
 
 pub type Result<T, E = Error> = result::Result<T, E>;
@@ -21,179 +23,179 @@
 #[derive(serde::Serialize, serde::Deserialize, Clone)]
 pub struct Source(pub Cow<'static, str>);
 impl fmt::Display for Source {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "<u>{}</u>", self.0)
-    }
+	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+		write!(f, "<u>{}</u>", self.0)
+	}
 }
 
 pub trait Prompter: Send + Sync {
-    fn prompt_radio(
-        &self,
-        prompt: &str,
-        description: &str,
-        source: &[Source],
-    ) -> impl Future<Output = Result<bool>> + Send {
-        let fut = self.prompt_enum(prompt, description, &["No", "Yes"], source);
-        async { fut.await.map(|v| v == 1) }
-    }
-    fn prompt_enum(
-        &self,
-        prompt: &str,
-        description: &str,
-        variants: &[&str],
-        source: &[Source],
-    ) -> impl Future<Output = Result<u32>> + Send;
-    fn prompt_text(
-        &self,
-        echo: bool,
-        prompt: &str,
-        description: &str,
-        source: &[Source],
-    ) -> impl Future<Output = Result<String>> + Send;
-    fn display_text(
-        &self,
-        error: bool,
-        description: &str,
-        source: &[Source],
-    ) -> impl Future<Output = Result<()>> + Send;
+	fn prompt_radio(
+		&self,
+		prompt: &str,
+		description: &str,
+		source: &[Source],
+	) -> impl Future<Output = Result<bool>> + Send {
+		let fut = self.prompt_enum(prompt, description, &["No", "Yes"], source);
+		async { fut.await.map(|v| v == 1) }
+	}
+	fn prompt_enum(
+		&self,
+		prompt: &str,
+		description: &str,
+		variants: &[&str],
+		source: &[Source],
+	) -> impl Future<Output = Result<u32>> + Send;
+	fn prompt_text(
+		&self,
+		echo: bool,
+		prompt: &str,
+		description: &str,
+		source: &[Source],
+	) -> impl Future<Output = Result<String>> + Send;
+	fn display_text(
+		&self,
+		error: bool,
+		description: &str,
+		source: &[Source],
+	) -> impl Future<Output = Result<()>> + Send;
 }
 pub trait BlockingPrompter {
-    fn prompt_radio(&self, prompt: &str, description: &str, source: &[Source]) -> Result<bool> {
-        self.prompt_enum(prompt, description, &["No", "Yes"], source)
-            .map(|v| v == 1)
-    }
-    fn prompt_enum(
-        &self,
-        prompt: &str,
-        description: &str,
-        variants: &[&str],
-        source: &[Source],
-    ) -> Result<u32>;
-    fn prompt_text(
-        &self,
-        echo: bool,
-        prompt: &str,
-        description: &str,
-        source: &[Source],
-    ) -> Result<String>;
-    fn display_text(&self, error: bool, description: &str, source: &[Source]) -> Result<()>;
+	fn prompt_radio(&self, prompt: &str, description: &str, source: &[Source]) -> Result<bool> {
+		self.prompt_enum(prompt, description, &["No", "Yes"], source)
+			.map(|v| v == 1)
+	}
+	fn prompt_enum(
+		&self,
+		prompt: &str,
+		description: &str,
+		variants: &[&str],
+		source: &[Source],
+	) -> Result<u32>;
+	fn prompt_text(
+		&self,
+		echo: bool,
+		prompt: &str,
+		description: &str,
+		source: &[Source],
+	) -> Result<String>;
+	fn display_text(&self, error: bool, description: &str, source: &[Source]) -> Result<()>;
 }
 impl<P> Prompter for &P
 where
-    P: Prompter,
+	P: Prompter,
 {
-    fn prompt_radio(
-        &self,
-        prompt: &str,
-        description: &str,
-        source: &[Source],
-    ) -> impl Future<Output = Result<bool>> + Send {
-        (*self).prompt_radio(prompt, description, source)
-    }
+	fn prompt_radio(
+		&self,
+		prompt: &str,
+		description: &str,
+		source: &[Source],
+	) -> impl Future<Output = Result<bool>> + Send {
+		(*self).prompt_radio(prompt, description, source)
+	}
 
-    fn prompt_enum(
-        &self,
-        prompt: &str,
-        description: &str,
-        variants: &[&str],
-        source: &[Source],
-    ) -> impl Future<Output = Result<u32>> + Send {
-        (*self).prompt_enum(prompt, description, variants, source)
-    }
+	fn prompt_enum(
+		&self,
+		prompt: &str,
+		description: &str,
+		variants: &[&str],
+		source: &[Source],
+	) -> impl Future<Output = Result<u32>> + Send {
+		(*self).prompt_enum(prompt, description, variants, source)
+	}
 
-    fn prompt_text(
-        &self,
-        echo: bool,
-        prompt: &str,
-        description: &str,
-        source: &[Source],
-    ) -> impl Future<Output = Result<String>> + Send {
-        (*self).prompt_text(echo, prompt, description, source)
-    }
+	fn prompt_text(
+		&self,
+		echo: bool,
+		prompt: &str,
+		description: &str,
+		source: &[Source],
+	) -> impl Future<Output = Result<String>> + Send {
+		(*self).prompt_text(echo, prompt, description, source)
+	}
 
-    fn display_text(
-        &self,
-        error: bool,
-        description: &str,
-        source: &[Source],
-    ) -> impl Future<Output = Result<()>> + Send {
-        (*self).display_text(error, description, source)
-    }
+	fn display_text(
+		&self,
+		error: bool,
+		description: &str,
+		source: &[Source],
+	) -> impl Future<Output = Result<()>> + Send {
+		(*self).display_text(error, description, source)
+	}
 }
 
 pub struct PrependSourcePrompter<P> {
-    pub prompter: P,
-    pub source: Vec<Source>,
-    pub description: String,
+	pub prompter: P,
+	pub source: Vec<Source>,
+	pub description: String,
 }
 impl<P> PrependSourcePrompter<P> {
-    fn source(&self, input: &[Source]) -> Vec<Source> {
-        let mut out = self.source.clone();
-        out.extend(input.iter().cloned());
-        out
-    }
-    fn description(&self, input: &str) -> String {
-        if self.description.is_empty() {
-            input.to_owned()
-        } else if input.is_empty() {
-            self.description.to_owned()
-        } else {
-            format!("{input}\n\n{}", self.description)
-        }
-    }
+	fn source(&self, input: &[Source]) -> Vec<Source> {
+		let mut out = self.source.clone();
+		out.extend(input.iter().cloned());
+		out
+	}
+	fn description(&self, input: &str) -> String {
+		if self.description.is_empty() {
+			input.to_owned()
+		} else if input.is_empty() {
+			self.description.to_owned()
+		} else {
+			format!("{input}\n\n{}", self.description)
+		}
+	}
 }
 impl<P> Prompter for PrependSourcePrompter<P>
 where
-    P: Prompter + Sync,
+	P: Prompter + Sync,
 {
-    async fn prompt_radio(
-        &self,
-        prompt: &str,
-        description: &str,
-        source: &[Source],
-    ) -> Result<bool> {
-        self.prompter
-            .prompt_radio(prompt, &self.description(description), &self.source(source))
-            .await
-    }
+	async fn prompt_radio(
+		&self,
+		prompt: &str,
+		description: &str,
+		source: &[Source],
+	) -> Result<bool> {
+		self.prompter
+			.prompt_radio(prompt, &self.description(description), &self.source(source))
+			.await
+	}
 
-    async fn prompt_enum(
-        &self,
-        prompt: &str,
-        description: &str,
-        variants: &[&str],
-        source: &[Source],
-    ) -> Result<u32> {
-        self.prompter
-            .prompt_enum(
-                prompt,
-                dbg!(&self.description(description)),
-                variants,
-                &self.source(source),
-            )
-            .await
-    }
+	async fn prompt_enum(
+		&self,
+		prompt: &str,
+		description: &str,
+		variants: &[&str],
+		source: &[Source],
+	) -> Result<u32> {
+		self.prompter
+			.prompt_enum(
+				prompt,
+				&self.description(description),
+				variants,
+				&self.source(source),
+			)
+			.await
+	}
 
-    async fn prompt_text(
-        &self,
-        echo: bool,
-        prompt: &str,
-        description: &str,
-        source: &[Source],
-    ) -> Result<String> {
-        self.prompter
-            .prompt_text(
-                echo,
-                prompt,
-                &self.description(description),
-                &self.source(source),
-            )
-            .await
-    }
+	async fn prompt_text(
+		&self,
+		echo: bool,
+		prompt: &str,
+		description: &str,
+		source: &[Source],
+	) -> Result<String> {
+		self.prompter
+			.prompt_text(
+				echo,
+				prompt,
+				&self.description(description),
+				&self.source(source),
+			)
+			.await
+	}
 
-    async fn display_text(&self, error: bool, description: &str, source: &[Source]) -> Result<()> {
-        self.prompter
-            .display_text(error, &self.description(description), &self.source(source))
-            .await
-    }
+	async fn display_text(&self, error: bool, description: &str, source: &[Source]) -> Result<()> {
+		self.prompter
+			.display_text(error, &self.description(description), &self.source(source))
+			.await
+	}
 }
modifiedcrates/ui-prompt/src/rofi.rsdiffbeforeafterboth
66
7use crate::{Error, Prompter, Result, Source};7use crate::{Error, Prompter, Result, Source};
88
9#[derive(Clone)]
9pub struct RofiPrompter;10pub struct RofiPrompter;
1011
11fn fixup_prompt(prompt: &str) -> &str {12fn fixup_prompt(prompt: &str) -> &str {
12 // Rofi always appends such suffix13 // Rofi always appends such suffix
13 prompt.strip_suffix(": ").unwrap_or(prompt)14 prompt.strip_suffix(": ").unwrap_or(prompt)
14}15}
16
17fn rofi_command() -> Command {
18 Command::new(option_env!("ROFI").unwrap_or("rofi"))
19}
1520
16impl Prompter for RofiPrompter {21impl Prompter for RofiPrompter {
17 async fn prompt_enum(22 async fn prompt_enum(
22 source: &[Source],27 source: &[Source],
23 ) -> Result<u32> {28 ) -> Result<u32> {
24 trace!("rofi radio");29 trace!("rofi radio");
25 let mut cmd = Command::new("rofi");30 let mut cmd = rofi_command();
26 let mesg = if source.is_empty() {31 let mesg = if source.is_empty() {
27 description.to_owned()32 description.to_owned()
28 } else {33 } else {
99 source: &[Source],104 source: &[Source],
100 ) -> Result<String> {105 ) -> Result<String> {
101 trace!("rofi text");106 trace!("rofi text");
102 let mut cmd = Command::new("rofi");107 let mut cmd = rofi_command();
103 let mesg = if source.is_empty() {108 let mesg = if source.is_empty() {
104 description.to_owned()109 description.to_owned()
105 } else {110 } else {
139144
140 async fn display_text(&self, error: bool, description: &str, source: &[Source]) -> Result<()> {145 async fn display_text(&self, error: bool, description: &str, source: &[Source]) -> Result<()> {
141 trace!("rofi display");146 trace!("rofi display");
142 let mut cmd = Command::new("rofi");147 let mut cmd = rofi_command();
143 let mut mesg = if source.is_empty() {148 let mut mesg = if source.is_empty() {
144 description.to_owned()149 description.to_owned()
145 } else {150 } else {
178 use crate::rofi::RofiPrompter;183 use crate::rofi::RofiPrompter;
179 use crate::{PrependSourcePrompter, Prompter as _, Source};184 use crate::{PrependSourcePrompter, Prompter as _, Source};
180185
186 // #[tokio::test]
181 #[tokio::test]187 #[tokio::test]
188 #[ignore = "interactive"]
182 async fn test() {189 async fn test() {
183 let prompter = PrependSourcePrompter {190 let prompter = PrependSourcePrompter {
184 prompter: RofiPrompter,191 prompter: RofiPrompter,
192 description: "test".to_owned(),
185 source: vec![Source(Cow::Borrowed("ssh"))],193 source: vec![Source(Cow::Borrowed("ssh"))],
186 };194 };
187 prompter195 prompter