Skip to content

Commit

Permalink
Add DBusMethodCall convenience trait
Browse files Browse the repository at this point in the history
This trait represents a parsed method call with deserialized arguments,
to abstract over call parsing.

Then add new registration builder helpers to register method calls with
a simplified callback which receives parsed arguments, and can
optionally return an async result.
  • Loading branch information
swsnr committed Oct 31, 2024
1 parent 5c9d780 commit 7ebb847
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 18 deletions.
32 changes: 19 additions & 13 deletions examples/gio_dbus_register_object/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use gio::{prelude::*, IOErrorEnum};
use gio::{prelude::*, DBusMethodCall, IOErrorEnum};
use std::{
sync::mpsc::{channel, Receiver, Sender},
time::Duration,
Expand Down Expand Up @@ -32,16 +32,21 @@ struct SlowHello {
}

#[derive(Debug)]
enum Call {
enum HelloMethod {
Hello(Hello),
SlowHello(SlowHello),
}

impl Call {
pub fn parse(method: &str, parameters: glib::Variant) -> Result<Call, glib::Error> {
impl DBusMethodCall for HelloMethod {
fn parse_call(
_obj_path: &str,
_interface: &str,
method: &str,
params: glib::Variant,
) -> Result<Self, glib::Error> {
match method {
"Hello" => Ok(parameters.get::<Hello>().map(Call::Hello)),
"SlowHello" => Ok(parameters.get::<SlowHello>().map(Call::SlowHello)),
"Hello" => Ok(params.get::<Hello>().map(HelloMethod::Hello)),
"SlowHello" => Ok(params.get::<SlowHello>().map(HelloMethod::SlowHello)),
_ => Err(glib::Error::new(IOErrorEnum::Failed, "No such method")),
}
.and_then(|p| p.ok_or_else(|| glib::Error::new(IOErrorEnum::Failed, "Invalid parameters")))
Expand All @@ -58,23 +63,24 @@ fn on_startup(app: &gio::Application, tx: &Sender<gio::RegistrationId>) {

if let Ok(id) = connection
.register_object("/com/github/gtk_rs/examples/HelloWorld", &example)
.method_call(move |_, _, _, _, method, params, invocation| {
let call = Call::parse(method, params);
invocation.return_future_local(async move {
match call? {
Call::Hello(Hello { name }) => {
.parse_method_call::<HelloMethod>()
.invoke_and_return_future_local(|_, sender, call| {
println!("Method call from {sender}");
async {
match call {
HelloMethod::Hello(Hello { name }) => {
let greet = format!("Hello {name}!");
println!("{greet}");
Ok(Some(greet.to_variant()))
}
Call::SlowHello(SlowHello { name, delay }) => {
HelloMethod::SlowHello(SlowHello { name, delay }) => {
glib::timeout_future(Duration::from_secs(delay as u64)).await;
let greet = format!("Hello {name} after {delay} seconds!");
println!("{greet}");
Ok(Some(greet.to_variant()))
}
}
});
}
})
.build()
{
Expand Down
95 changes: 92 additions & 3 deletions gio/src/dbus_connection.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,95 @@
// Take a look at the license at the top of the repository in the LICENSE file.

use std::{boxed::Box as Box_, num::NonZeroU32};

use glib::{prelude::*, translate::*};
use std::{boxed::Box as Box_, future::Future, marker::PhantomData, num::NonZeroU32};

use crate::{
ffi, ActionGroup, DBusConnection, DBusInterfaceInfo, DBusMessage, DBusMethodInvocation,
DBusSignalFlags, MenuModel,
};
use glib::{prelude::*, translate::*};

pub trait DBusMethodCall: Sized {
fn parse_call(
obj_path: &str,
interface: &str,
method: &str,
params: glib::Variant,
) -> Result<Self, glib::Error>;
}

pub struct MethodCallBuilder<'a, T> {
registration: RegistrationBuilder<'a>,
capture_type: PhantomData<T>,
}

impl<'a, T: DBusMethodCall> MethodCallBuilder<'a, T> {
/// Handle invocation of a parsed method call.
///
/// For each DBus method call parse the call, and then invoke the given closure
/// with
///
/// 1. the DBus connection object,
/// 2. the name of the sender of the method call,
/// 3. the parsed call, and
/// 4. the method invocation object.
///
/// The closure **must** return a value through the invocation object,
/// using any of its `return_` functions, such as
/// [`DBusMethodInvocation::return_result`] or
/// [`DBusMethodInvocation::return_future_local`], to finish the call.
pub fn invoke<F>(self, f: F) -> RegistrationBuilder<'a>
where
F: Fn(DBusConnection, &str, T, DBusMethodInvocation) + 'static,
{
self.registration.method_call(
move |connection, sender, obj_path, interface, method, params, invocation| {
match T::parse_call(obj_path, interface, method, params) {
Ok(call) => f(connection, sender, call, invocation),
Err(error) => invocation.return_gerror(error),
}
},
)
}

/// Handle invocation of a parsed method call.
///
/// For each DBus method call parse the call, and then invoke the given closure
/// with
///
/// 1. the DBus connection object,
/// 2. the name of the sender of the method call, and
/// 3. the parsed call.
///
/// The return value of the closure is then returned on the method call.
pub fn invoke_and_return<F>(self, f: F) -> RegistrationBuilder<'a>
where
F: Fn(DBusConnection, &str, T) -> Result<Option<glib::Variant>, glib::Error> + 'static,
{
self.invoke(move |connection, sender, call, invocation| {
invocation.return_result(f(connection, sender, call))
})
}

/// Handle an async invocation of a parsed method call.
///
/// For each DBus method call parse the call, and then invoke the given closure
/// with
///
/// 1. the DBus connection object,
/// 2. the name of the sender of the method call, and
/// 3. the parsed call.
///
/// The return value of the closure is then returned on the method call.
pub fn invoke_and_return_future_local<F, Fut>(self, f: F) -> RegistrationBuilder<'a>
where
F: Fn(DBusConnection, &str, T) -> Fut + 'static,
Fut: Future<Output = Result<Option<glib::Variant>, glib::Error>> + 'static,
{
self.invoke(move |connection, sender, call, invocation| {
invocation.return_future_local(f(connection, sender, call));
})
}
}

#[derive(Debug, Eq, PartialEq)]
pub struct RegistrationId(NonZeroU32);
Expand Down Expand Up @@ -49,6 +131,13 @@ impl<'a> RegistrationBuilder<'a> {
self
}

pub fn parse_method_call<T: DBusMethodCall>(self) -> MethodCallBuilder<'a, T> {
MethodCallBuilder {
registration: self,
capture_type: Default::default(),
}
}

#[doc(alias = "get_property")]
pub fn property<F: Fn(DBusConnection, &str, &str, &str, &str) -> glib::Variant + 'static>(
mut self,
Expand Down
4 changes: 2 additions & 2 deletions gio/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ mod dbus;
pub use self::dbus::*;
mod dbus_connection;
pub use self::dbus_connection::{
ActionGroupExportId, FilterId, MenuModelExportId, RegistrationId, SignalSubscriptionId,
WatcherId,
ActionGroupExportId, DBusMethodCall, FilterId, MenuModelExportId, RegistrationId,
SignalSubscriptionId, WatcherId,
};
mod dbus_message;
mod dbus_method_invocation;
Expand Down

0 comments on commit 7ebb847

Please sign in to comment.