Skip to content

Commit 17db771

Browse files
swsnrbilelmoussaoui
authored andcommitted
Add DBusMethodCall convenience trait
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.
1 parent 76acf24 commit 17db771

File tree

3 files changed

+139
-20
lines changed

3 files changed

+139
-20
lines changed

examples/gio_dbus_register_object/main.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,21 @@ struct SlowHello {
3232
}
3333

3434
#[derive(Debug)]
35-
enum Call {
35+
enum HelloMethod {
3636
Hello(Hello),
3737
SlowHello(SlowHello),
3838
}
3939

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

5964
if let Ok(id) = connection
6065
.register_object("/com/github/gtk_rs/examples/HelloWorld", &example)
61-
.method_call(move |_, _, _, _, method, params, invocation| {
62-
let call = Call::parse(method, params);
63-
invocation.return_future_local(async move {
64-
match call? {
65-
Call::Hello(Hello { name }) => {
66+
.typed_method_call::<HelloMethod>()
67+
.invoke_and_return_future_local(|_, sender, call| {
68+
println!("Method call from {sender}");
69+
async {
70+
match call {
71+
HelloMethod::Hello(Hello { name }) => {
6672
let greet = format!("Hello {name}!");
6773
println!("{greet}");
6874
Ok(Some(greet.to_variant()))
6975
}
70-
Call::SlowHello(SlowHello { name, delay }) => {
76+
HelloMethod::SlowHello(SlowHello { name, delay }) => {
7177
glib::timeout_future(Duration::from_secs(delay as u64)).await;
7278
let greet = format!("Hello {name} after {delay} seconds!");
7379
println!("{greet}");
7480
Ok(Some(greet.to_variant()))
7581
}
7682
}
77-
});
83+
}
7884
})
7985
.build()
8086
{

gio/src/dbus_connection.rs

Lines changed: 116 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,110 @@
11
// Take a look at the license at the top of the repository in the LICENSE file.
22

3-
use std::{boxed::Box as Box_, num::NonZeroU32};
4-
5-
use glib::{prelude::*, translate::*};
3+
use std::{boxed::Box as Box_, future::Future, marker::PhantomData, num::NonZeroU32};
64

75
use crate::{
86
ffi, ActionGroup, DBusConnection, DBusInterfaceInfo, DBusMessage, DBusMethodInvocation,
97
DBusSignalFlags, MenuModel,
108
};
9+
use glib::{prelude::*, translate::*};
10+
11+
pub trait DBusMethodCall: Sized {
12+
fn parse_call(
13+
obj_path: &str,
14+
interface: &str,
15+
method: &str,
16+
params: glib::Variant,
17+
) -> Result<Self, glib::Error>;
18+
}
19+
20+
// rustdoc-stripper-ignore-next
21+
/// Handle method invocations.
22+
pub struct MethodCallBuilder<'a, T> {
23+
registration: RegistrationBuilder<'a>,
24+
capture_type: PhantomData<T>,
25+
}
26+
27+
impl<'a, T: DBusMethodCall> MethodCallBuilder<'a, T> {
28+
// rustdoc-stripper-ignore-next
29+
/// Handle invocation of a parsed method call.
30+
///
31+
/// For each DBus method call parse the call, and then invoke the given closure
32+
/// with
33+
///
34+
/// 1. the DBus connection object,
35+
/// 2. the name of the sender of the method call,
36+
/// 3. the parsed call, and
37+
/// 4. the method invocation object.
38+
///
39+
/// The closure **must** return a value through the invocation object in all
40+
/// code paths, using any of its `return_` functions, such as
41+
/// [`DBusMethodInvocation::return_result`] or
42+
/// [`DBusMethodInvocation::return_future_local`], to finish the call.
43+
///
44+
/// If direct access to the invocation object is not needed,
45+
/// [`invoke_and_return`] and [`invoke_and_return_future_local`] provide a
46+
/// safer interface where the callback returns a result directly.
47+
pub fn invoke<F>(self, f: F) -> RegistrationBuilder<'a>
48+
where
49+
F: Fn(DBusConnection, &str, T, DBusMethodInvocation) + 'static,
50+
{
51+
self.registration.method_call(
52+
move |connection, sender, obj_path, interface, method, params, invocation| {
53+
match T::parse_call(obj_path, interface, method, params) {
54+
Ok(call) => f(connection, sender, call, invocation),
55+
Err(error) => invocation.return_gerror(error),
56+
}
57+
},
58+
)
59+
}
60+
61+
// rustdoc-stripper-ignore-next
62+
/// Handle invocation of a parsed method call.
63+
///
64+
/// For each DBus method call parse the call, and then invoke the given closure
65+
/// with
66+
///
67+
/// 1. the DBus connection object,
68+
/// 2. the name of the sender of the method call, and
69+
/// 3. the parsed call.
70+
///
71+
/// The return value of the closure is then returned on the method call.
72+
/// If the returned variant value is not a tuple, it is automatically wrapped
73+
/// in a single element tuple, as DBus methods must always return tuples.
74+
/// See [`DBusMethodInvocation::return_result`] for details.
75+
pub fn invoke_and_return<F>(self, f: F) -> RegistrationBuilder<'a>
76+
where
77+
F: Fn(DBusConnection, &str, T) -> Result<Option<glib::Variant>, glib::Error> + 'static,
78+
{
79+
self.invoke(move |connection, sender, call, invocation| {
80+
invocation.return_result(f(connection, sender, call))
81+
})
82+
}
83+
84+
// rustdoc-stripper-ignore-next
85+
/// Handle an async invocation of a parsed method call.
86+
///
87+
/// For each DBus method call parse the call, and then invoke the given closure
88+
/// with
89+
///
90+
/// 1. the DBus connection object,
91+
/// 2. the name of the sender of the method call, and
92+
/// 3. the parsed call.
93+
///
94+
/// The output of the future is then returned on the method call.
95+
/// If the returned variant value is not a tuple, it is automatically wrapped
96+
/// in a single element tuple, as DBus methods must always return tuples.
97+
/// See [`DBusMethodInvocation::return_future_local`] for details.
98+
pub fn invoke_and_return_future_local<F, Fut>(self, f: F) -> RegistrationBuilder<'a>
99+
where
100+
F: Fn(DBusConnection, &str, T) -> Fut + 'static,
101+
Fut: Future<Output = Result<Option<glib::Variant>, glib::Error>> + 'static,
102+
{
103+
self.invoke(move |connection, sender, call, invocation| {
104+
invocation.return_future_local(f(connection, sender, call));
105+
})
106+
}
107+
}
11108

12109
#[derive(Debug, Eq, PartialEq)]
13110
pub struct RegistrationId(NonZeroU32);
@@ -22,6 +119,8 @@ pub struct FilterId(NonZeroU32);
22119
#[derive(Debug, Eq, PartialEq)]
23120
pub struct SignalSubscriptionId(NonZeroU32);
24121

122+
// rustdoc-stripper-ignore-next
123+
/// Build a registered DBus object, by handling different parts of DBus.
25124
#[must_use = "The builder must be built to be used"]
26125
pub struct RegistrationBuilder<'a> {
27126
connection: &'a DBusConnection,
@@ -38,7 +137,7 @@ pub struct RegistrationBuilder<'a> {
38137
Option<Box_<dyn Fn(DBusConnection, &str, &str, &str, &str, glib::Variant) -> bool>>,
39138
}
40139

41-
impl RegistrationBuilder<'_> {
140+
impl<'a> RegistrationBuilder<'a> {
42141
pub fn method_call<
43142
F: Fn(DBusConnection, &str, &str, &str, &str, glib::Variant, DBusMethodInvocation) + 'static,
44143
>(
@@ -49,6 +148,19 @@ impl RegistrationBuilder<'_> {
49148
self
50149
}
51150

151+
// rustdoc-stripper-ignore-next
152+
/// Handle method calls on this object.
153+
///
154+
/// Return a builder for method calls which parses method names and
155+
/// parameters with the given [`DBusMethodCall`] and then allows to dispatch
156+
/// the parsed call either synchronously or asynchronously.
157+
pub fn typed_method_call<T: DBusMethodCall>(self) -> MethodCallBuilder<'a, T> {
158+
MethodCallBuilder {
159+
registration: self,
160+
capture_type: Default::default(),
161+
}
162+
}
163+
52164
#[doc(alias = "get_property")]
53165
pub fn property<F: Fn(DBusConnection, &str, &str, &str, &str) -> glib::Variant + 'static>(
54166
mut self,

gio/src/prelude.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@ pub use crate::{
3535
action_map::ActionMapExtManual, application::ApplicationExtManual, auto::traits::*,
3636
cancellable::CancellableExtManual, converter::ConverterExtManual,
3737
data_input_stream::DataInputStreamExtManual, datagram_based::DatagramBasedExtManual,
38-
dbus_proxy::DBusProxyExtManual, file::FileExtManual, file_enumerator::FileEnumeratorExtManual,
39-
inet_address::InetAddressExtManual, input_stream::InputStreamExtManual,
40-
io_stream::IOStreamExtManual, list_model::ListModelExtManual,
41-
output_stream::OutputStreamExtManual, pollable_input_stream::PollableInputStreamExtManual,
38+
dbus_connection::DBusMethodCall, dbus_proxy::DBusProxyExtManual, file::FileExtManual,
39+
file_enumerator::FileEnumeratorExtManual, inet_address::InetAddressExtManual,
40+
input_stream::InputStreamExtManual, io_stream::IOStreamExtManual,
41+
list_model::ListModelExtManual, output_stream::OutputStreamExtManual,
42+
pollable_input_stream::PollableInputStreamExtManual,
4243
pollable_output_stream::PollableOutputStreamExtManual, settings::SettingsExtManual,
4344
simple_proxy_resolver::SimpleProxyResolverExtManual, socket::SocketExtManual,
4445
socket_control_message::SocketControlMessageExtManual,

0 commit comments

Comments
 (0)