Skip to content

Commit

Permalink
Merge pull request #1584 from sdroege/gdbus-method-call-nullability
Browse files Browse the repository at this point in the history
gio: Fix nullability of various DBus method call related parameters
  • Loading branch information
sdroege authored Dec 1, 2024
2 parents 478854c + b7be698 commit 9f09c42
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 19 deletions.
4 changes: 2 additions & 2 deletions examples/gio_dbus_register_object/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ enum HelloMethod {
impl DBusMethodCall for HelloMethod {
fn parse_call(
_obj_path: &str,
_interface: &str,
_interface: Option<&str>,
method: &str,
params: glib::Variant,
) -> Result<Self, glib::Error> {
Expand All @@ -65,7 +65,7 @@ fn on_startup(app: &gio::Application, tx: &Sender<gio::RegistrationId>) {
.register_object("/com/github/gtk_rs/examples/HelloWorld", &example)
.typed_method_call::<HelloMethod>()
.invoke_and_return_future_local(|_, sender, call| {
println!("Method call from {sender}");
println!("Method call from {sender:?}");
async {
match call {
HelloMethod::Hello(Hello { name }) => {
Expand Down
11 changes: 10 additions & 1 deletion gio/Gir.toml
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,16 @@ status = "generate"
name = "return_error_literal"
# glib::ErrorDomain
manual = true

[[object.function]]
name = "get_sender"
[object.function.return]
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4414
nullable = true
[[object.function]]
name = "get_interface_name"
[object.function.return]
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4414
nullable = true

[[object]]
name = "Gio.DBusProxy"
Expand Down
4 changes: 2 additions & 2 deletions gio/src/auto/dbus_method_invocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl DBusMethodInvocation {

#[doc(alias = "g_dbus_method_invocation_get_interface_name")]
#[doc(alias = "get_interface_name")]
pub fn interface_name(&self) -> glib::GString {
pub fn interface_name(&self) -> Option<glib::GString> {
unsafe {
from_glib_none(ffi::g_dbus_method_invocation_get_interface_name(
self.to_glib_none().0,
Expand Down Expand Up @@ -103,7 +103,7 @@ impl DBusMethodInvocation {

#[doc(alias = "g_dbus_method_invocation_get_sender")]
#[doc(alias = "get_sender")]
pub fn sender(&self) -> glib::GString {
pub fn sender(&self) -> Option<glib::GString> {
unsafe {
from_glib_none(ffi::g_dbus_method_invocation_get_sender(
self.to_glib_none().0,
Expand Down
50 changes: 36 additions & 14 deletions gio/src/dbus_connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use glib::{prelude::*, translate::*};
pub trait DBusMethodCall: Sized {
fn parse_call(
obj_path: &str,
interface: &str,
interface: Option<&str>,
method: &str,
params: glib::Variant,
) -> Result<Self, glib::Error>;
Expand Down Expand Up @@ -46,7 +46,7 @@ impl<'a, T: DBusMethodCall> MethodCallBuilder<'a, T> {
/// safer interface where the callback returns a result directly.
pub fn invoke<F>(self, f: F) -> RegistrationBuilder<'a>
where
F: Fn(DBusConnection, &str, T, DBusMethodInvocation) + 'static,
F: Fn(DBusConnection, Option<&str>, T, DBusMethodInvocation) + 'static,
{
self.registration.method_call(
move |connection, sender, obj_path, interface, method, params, invocation| {
Expand Down Expand Up @@ -74,7 +74,8 @@ impl<'a, T: DBusMethodCall> MethodCallBuilder<'a, T> {
/// See [`DBusMethodInvocation::return_result`] for details.
pub fn invoke_and_return<F>(self, f: F) -> RegistrationBuilder<'a>
where
F: Fn(DBusConnection, &str, T) -> Result<Option<glib::Variant>, glib::Error> + 'static,
F: Fn(DBusConnection, Option<&str>, T) -> Result<Option<glib::Variant>, glib::Error>
+ 'static,
{
self.invoke(move |connection, sender, call, invocation| {
invocation.return_result(f(connection, sender, call))
Expand All @@ -97,7 +98,7 @@ impl<'a, T: DBusMethodCall> MethodCallBuilder<'a, T> {
/// See [`DBusMethodInvocation::return_future_local`] for details.
pub fn invoke_and_return_future_local<F, Fut>(self, f: F) -> RegistrationBuilder<'a>
where
F: Fn(DBusConnection, &str, T) -> Fut + 'static,
F: Fn(DBusConnection, Option<&str>, T) -> Fut + 'static,
Fut: Future<Output = Result<Option<glib::Variant>, glib::Error>> + 'static,
{
self.invoke(move |connection, sender, call, invocation| {
Expand Down Expand Up @@ -128,18 +129,37 @@ pub struct RegistrationBuilder<'a> {
interface_info: &'a DBusInterfaceInfo,
#[allow(clippy::type_complexity)]
method_call: Option<
Box_<dyn Fn(DBusConnection, &str, &str, &str, &str, glib::Variant, DBusMethodInvocation)>,
Box_<
dyn Fn(
DBusConnection,
Option<&str>,
&str,
Option<&str>,
&str,
glib::Variant,
DBusMethodInvocation,
),
>,
>,
#[allow(clippy::type_complexity)]
get_property: Option<Box_<dyn Fn(DBusConnection, &str, &str, &str, &str) -> glib::Variant>>,
get_property:
Option<Box_<dyn Fn(DBusConnection, Option<&str>, &str, &str, &str) -> glib::Variant>>,
#[allow(clippy::type_complexity)]
set_property:
Option<Box_<dyn Fn(DBusConnection, &str, &str, &str, &str, glib::Variant) -> bool>>,
Option<Box_<dyn Fn(DBusConnection, Option<&str>, &str, &str, &str, glib::Variant) -> bool>>,
}

impl<'a> RegistrationBuilder<'a> {
pub fn method_call<
F: Fn(DBusConnection, &str, &str, &str, &str, glib::Variant, DBusMethodInvocation) + 'static,
F: Fn(
DBusConnection,
Option<&str>,
&str,
Option<&str>,
&str,
glib::Variant,
DBusMethodInvocation,
) + 'static,
>(
mut self,
f: F,
Expand All @@ -162,7 +182,9 @@ impl<'a> RegistrationBuilder<'a> {
}

#[doc(alias = "get_property")]
pub fn property<F: Fn(DBusConnection, &str, &str, &str, &str) -> glib::Variant + 'static>(
pub fn property<
F: Fn(DBusConnection, Option<&str>, &str, &str, &str) -> glib::Variant + 'static,
>(
mut self,
f: F,
) -> Self {
Expand All @@ -171,7 +193,7 @@ impl<'a> RegistrationBuilder<'a> {
}

pub fn set_property<
F: Fn(DBusConnection, &str, &str, &str, &str, glib::Variant) -> bool + 'static,
F: Fn(DBusConnection, Option<&str>, &str, &str, &str, glib::Variant) -> bool + 'static,
>(
mut self,
f: F,
Expand All @@ -191,9 +213,9 @@ impl<'a> RegistrationBuilder<'a> {
.map(|f| {
glib::Closure::new_local(move |args| {
let conn = args[0].get::<DBusConnection>().unwrap();
let sender = args[1].get::<&str>().unwrap();
let sender = args[1].get::<Option<&str>>().unwrap();
let object_path = args[2].get::<&str>().unwrap();
let interface_name = args[3].get::<&str>().unwrap();
let interface_name = args[3].get::<Option<&str>>().unwrap();
let method_name = args[4].get::<&str>().unwrap();
let parameters = args[5].get::<glib::Variant>().unwrap();
let invocation = args[6].get::<DBusMethodInvocation>().unwrap();
Expand All @@ -215,7 +237,7 @@ impl<'a> RegistrationBuilder<'a> {
.map(|f| {
glib::Closure::new_local(move |args| {
let conn = args[0].get::<DBusConnection>().unwrap();
let sender = args[1].get::<&str>().unwrap();
let sender = args[1].get::<Option<&str>>().unwrap();
let object_path = args[2].get::<&str>().unwrap();
let interface_name = args[3].get::<&str>().unwrap();
let property_name = args[4].get::<&str>().unwrap();
Expand All @@ -237,7 +259,7 @@ impl<'a> RegistrationBuilder<'a> {
.map(|f| {
glib::Closure::new_local(move |args| {
let conn = args[0].get::<DBusConnection>().unwrap();
let sender = args[1].get::<&str>().unwrap();
let sender = args[1].get::<Option<&str>>().unwrap();
let object_path = args[2].get::<&str>().unwrap();
let interface_name = args[3].get::<&str>().unwrap();
let property_name = args[4].get::<&str>().unwrap();
Expand Down
157 changes: 157 additions & 0 deletions gio/tests/dbus_peer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// Take a look at the license at the top of the repository in the LICENSE file.

#[cfg(unix)]
#[test]
fn test_gdbus_peer_connection() {
use gio::{
glib::{self, VariantTy},
prelude::*,
DBusConnection, DBusConnectionFlags, DBusNodeInfo, Socket,
};
use std::os::{fd::IntoRawFd, unix::net::UnixStream};

const EXAMPLE_XML: &str = r#"
<node>
<interface name='com.github.gtk_rs'>
<method name='Hello'>
<arg type='s' name='name' direction='in'/>
<arg type='s' name='greet' direction='out'/>
</method>
</interface>
</node>
"#;

pub async fn spawn_server(fd: UnixStream) -> DBusConnection {
let socket = unsafe { Socket::from_fd(fd.into_raw_fd()) }.unwrap();
let socket_connection = socket.connection_factory_create_connection();

let guid = gio::dbus_generate_guid();

dbg!("server connecting");

let connection = DBusConnection::new_future(
&socket_connection,
Some(&guid),
DBusConnectionFlags::AUTHENTICATION_SERVER
.union(DBusConnectionFlags::DELAY_MESSAGE_PROCESSING),
None,
)
.await
.unwrap();

dbg!("server connected");

let interface_info = DBusNodeInfo::for_xml(EXAMPLE_XML)
.unwrap()
.lookup_interface("com.github.gtk_rs")
.unwrap();

let _id = connection
.register_object("/com/github/gtk_rs", &interface_info)
.method_call(
|_connection,
_sender,
_object_path,
_interface_name,
_method_name,
parameters,
invocation| {
dbg!(
_sender,
_object_path,
_interface_name,
_method_name,
&parameters,
&invocation
);

let name = parameters.child_get::<String>(0);
invocation.return_value(Some(&(format!("Hello {name}!"),).to_variant()));
},
)
.build()
.unwrap();

dbg!("server starts message processing");

connection.start_message_processing();

dbg!("server awaiting calls");

connection
}

pub async fn spawn_client(fd: UnixStream) -> DBusConnection {
let socket_client = unsafe { Socket::from_fd(fd.into_raw_fd()) }.unwrap();
let socket_connection_client = socket_client.connection_factory_create_connection();

dbg!("client connecting");

let connection = DBusConnection::new_future(
&socket_connection_client,
None,
DBusConnectionFlags::AUTHENTICATION_CLIENT,
None,
)
.await
.unwrap();

dbg!("client connected");

connection
}

let ctx = glib::MainContext::default();

let (x, y) = std::os::unix::net::UnixStream::pair().unwrap();

x.set_nonblocking(true).unwrap();
y.set_nonblocking(true).unwrap();

ctx.block_on(async move {
let ctx = glib::MainContext::default();

let server = ctx.spawn_local(spawn_server(x));
let client = ctx.spawn_local(spawn_client(y));

let server = server.await.unwrap();
let client = client.await.unwrap();

dbg!("calling method");

let result = client
.call_future(
None,
"/com/github/gtk_rs",
"com.github.gtk_rs",
"Hello",
Some(&("World",).into()),
Some(VariantTy::new("(s)").unwrap()),
gio::DBusCallFlags::NONE,
10000,
)
.await
.unwrap();

dbg!("method called");

dbg!(&result);

dbg!("closing client");
client.close_future().await.unwrap();
dbg!("closed client, closing server");
server.close_future().await.unwrap();
dbg!("closed server");

drop(client);
drop(server);

assert_eq!(result.child_get::<String>(0), "Hello World!");

glib::timeout_future_with_priority(
glib::Priority::LOW,
std::time::Duration::from_millis(50),
)
.await;
});
}

0 comments on commit 9f09c42

Please sign in to comment.