search_provider/
search_provider.rs
use std::cell::RefCell;
use futures_channel::{
mpsc::{Receiver, Sender},
oneshot,
};
use futures_util::StreamExt;
use zbus::names::WellKnownName;
use zbus::zvariant::ObjectPath;
use crate::{ResultID, ResultMeta, SearchProviderImpl};
enum Action {
ActivateResult(ResultID, Vec<String>, u32),
InitialResult(Vec<String>, oneshot::Sender<Vec<String>>),
SubsearchResult(Vec<ResultID>, Vec<String>, oneshot::Sender<Vec<String>>),
ResultsMeta(Vec<ResultID>, oneshot::Sender<Vec<ResultMeta>>),
LaunchSearch(Vec<String>, u32),
}
pub struct SearchProvider<T>
where
T: SearchProviderImpl + 'static,
{
receiver: RefCell<Option<Receiver<Action>>>,
imp: T,
}
impl<T> SearchProvider<T>
where
T: SearchProviderImpl + 'static,
{
pub async fn new<N: TryInto<WellKnownName<'static>>, P: TryInto<ObjectPath<'static>>>(
imp: T,
name: N,
path: P,
) -> zbus::Result<Self>
where
zbus::Error: From<<N as TryInto<WellKnownName<'static>>>::Error>,
zbus::Error: From<<P as TryInto<ObjectPath<'static>>>::Error>,
{
let (sender, receiver) = futures_channel::mpsc::channel(10);
let iface = SearchProviderInterface::new(sender);
let cnx = zbus::Connection::session().await.unwrap();
let object_server = cnx.object_server();
let proxy = zbus::fdo::DBusProxy::builder(&cnx)
.cache_properties(zbus::proxy::CacheProperties::No)
.build()
.await?;
proxy
.request_name(
name.try_into()?,
zbus::fdo::RequestNameFlags::ReplaceExisting.into(),
)
.await?;
object_server.at(path.try_into()?, iface).await?;
let mut provider = Self {
receiver: RefCell::new(Some(receiver)),
imp,
};
provider.wait().await?;
Ok(provider)
}
async fn wait(&mut self) -> zbus::fdo::Result<()> {
let mut receiver = self.receiver.borrow_mut().take().unwrap();
loop {
let response = receiver.next().await;
match response {
Some(Action::ActivateResult(identifier, terms, timestamp)) => {
self.imp.activate_result(identifier, &terms, timestamp);
}
Some(Action::InitialResult(terms, sender)) => {
let results = self.imp.initial_result_set(&terms);
let _ = sender.send(results);
}
Some(Action::SubsearchResult(previous_terms, terms, sender)) => {
let results = self.imp.subsearch_result_set(&previous_terms, &terms);
let _ = sender.send(results);
}
Some(Action::ResultsMeta(identifiers, sender)) => {
let results = self.imp.result_metas(identifiers.as_slice());
let _ = sender.send(results);
}
Some(Action::LaunchSearch(terms, timestamp)) => {
self.imp.launch_search(&terms, timestamp);
}
None => (),
}
}
}
}
struct SearchProviderInterface {
sender: Sender<Action>,
}
impl SearchProviderInterface {
pub fn new(sender: Sender<Action>) -> Self {
Self { sender }
}
}
#[zbus::interface(name = "org.gnome.Shell.SearchProvider2")]
impl SearchProviderInterface {
async fn activate_result(
&mut self,
identifier: ResultID,
terms: Vec<String>,
timestamp: u32,
) -> zbus::fdo::Result<()> {
let _ = self
.sender
.try_send(Action::ActivateResult(identifier, terms, timestamp));
Ok(())
}
async fn get_initial_result_set(
&mut self,
terms: Vec<String>,
) -> zbus::fdo::Result<Vec<ResultID>> {
let (sender, receiver) = futures_channel::oneshot::channel();
let _ = self.sender.try_send(Action::InitialResult(terms, sender));
let response = receiver.await.unwrap();
Ok(response)
}
async fn get_subsearch_result_set(
&mut self,
previous_results: Vec<ResultID>,
terms: Vec<String>,
) -> zbus::fdo::Result<Vec<ResultID>> {
let (sender, receiver) = futures_channel::oneshot::channel();
let _ = self
.sender
.try_send(Action::SubsearchResult(previous_results, terms, sender));
let response = receiver.await.unwrap();
Ok(response)
}
async fn get_result_metas(
&mut self,
identifiers: Vec<ResultID>,
) -> zbus::fdo::Result<Vec<ResultMeta>> {
let (sender, receiver) = futures_channel::oneshot::channel();
let _ = self
.sender
.try_send(Action::ResultsMeta(identifiers, sender));
let response = receiver.await.unwrap();
Ok(response)
}
async fn launch_search(&mut self, terms: Vec<String>, timestamp: u32) -> zbus::fdo::Result<()> {
let _ = self.sender.try_send(Action::LaunchSearch(terms, timestamp));
Ok(())
}
}