Skip to content

Commit 852c187

Browse files
authored
Merge pull request #838 from zeenix/interface-gen-proxy
✨ zm: interface can now generate proxy for you
2 parents fa70bbc + a8698d5 commit 852c187

File tree

7 files changed

+408
-188
lines changed

7 files changed

+408
-188
lines changed

book/src/server.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,8 +353,81 @@ iface.greeter_name_changed(iface_ref.signal_context()).await?;
353353
# }
354354
```
355355

356+
## Proxy generation
357+
358+
`interface` macro can also generate the client-side proxy code for you. It utilizes the [`proxy`]
359+
macro behind the scenes to achieve this. Here is how to use it:
360+
361+
```rust
362+
use zbus::interface;
363+
364+
struct Greeter {
365+
name: String
366+
}
367+
368+
#[interface(
369+
name = "org.zbus.MyGreeter.WithProxy",
370+
// Specifying the `proxy` attribute instructs `interface` to generate the
371+
// client-side proxy. You can specify proxy-specific attributes
372+
// (e.g `gen_blocking) here. All the attributes that are common between
373+
// `proxy` and `interface` macros (e.g `name`) are automtically forwarded to
374+
// the `proxy` macro.
375+
proxy(
376+
gen_blocking = false,
377+
default_path = "/org/zbus/MyGreeter/WithProxy",
378+
default_service = "org.zbus.MyGreeter.WithProxy",
379+
),
380+
)]
381+
impl Greeter {
382+
#[zbus(property)]
383+
async fn greeter_name(&self) -> String {
384+
self.name.clone()
385+
}
386+
387+
#[zbus(proxy(no_reply))]
388+
async fn whatever(&self) {
389+
println!("Whatever!");
390+
}
391+
}
392+
393+
# #[tokio::main]
394+
# async fn main() -> zbus::Result<()> {
395+
396+
let greeter = Greeter { name: "GreeterName".to_string() };
397+
let connection = zbus::connection::Builder::session()?
398+
.name("org.zbus.MyGreeter.WithProxy")?
399+
.serve_at("/org/zbus/MyGreeter/WithProxy", greeter)?
400+
.build()
401+
.await?;
402+
let proxy = GreeterProxy::new(&connection).await?;
403+
assert_eq!(proxy.greeter_name().await?, "GreeterName");
404+
proxy.whatever().await?;
405+
406+
# Ok(())
407+
# }
408+
```
409+
410+
### Known Limitations
411+
412+
While it's extremely useful to be able to generate the client-side proxy code directly from
413+
`interface` as it allows you to avoid duplicating code, there are some limitations to be aware of:
414+
415+
* The trait bounds of the `proxy` macro methods' arguments and return value, now also apply to the
416+
`interface` methods. For example, when only generating the server-side code, the method return
417+
values need to implement `serde::Serialize` but when generating the client-side proxy code, the
418+
method return values need to implement `serde::DeserializeOwned` as well.
419+
* Reference types in return values of `interface` methods won't work. As you may have noticed,
420+
unlike the previous examples the `greeter_name` method in the example above returns a `String`
421+
instead of a `&str`. This is because the methods in the `proxy` macro do not support reference
422+
type to be returned from its methods.
423+
* Methods returning [`object_server::ResponseDispatchNotifier`] wrapper type will do the same for
424+
proxy as well.
425+
* Only `interface` macro supports this feature, while the deprecated `dbus_interface` macro does
426+
not. Still haven't switched to `interface` and want to use this feature? Time to switch!
427+
356428
[D-Bus concepts]: concepts.html#bus-name--service-name
357429
[didoc]: https://docs.rs/zbus/4/zbus/attr.interface.html
358430
[`zbus::DBusError`]:https://docs.rs/zbus/4/zbus/trait.DBusError.html
359431
[`zbus::fdo::Error`]: https://docs.rs/zbus/4/zbus/fdo/enum.Error.html
360432
[`zbus::fdo::Error::UnknownProperty`]: https://docs.rs/zbus/4/zbus/fdo/enum.Error.html#variant.UnknownProperty
433+
[`proxy`]: https://docs.rs/zbus/4/zbus/attr.proxy.html

zbus/src/object_server/mod.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! The object server API.
22
33
use event_listener::{Event, EventListener};
4-
use serde::Serialize;
4+
use serde::{Deserialize, Serialize};
55
use std::{
66
collections::{hash_map::Entry, HashMap},
77
fmt::Write,
@@ -856,6 +856,11 @@ impl<R> ResponseDispatchNotifier<R> {
856856
listener,
857857
)
858858
}
859+
860+
/// Get the response.
861+
pub fn response(&self) -> &R {
862+
&self.response
863+
}
859864
}
860865

861866
impl<R> Serialize for ResponseDispatchNotifier<R>
@@ -870,6 +875,21 @@ where
870875
}
871876
}
872877

878+
impl<'de, R> Deserialize<'de> for ResponseDispatchNotifier<R>
879+
where
880+
R: Deserialize<'de>,
881+
{
882+
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
883+
where
884+
D: serde::Deserializer<'de>,
885+
{
886+
Ok(Self {
887+
response: R::deserialize(deserializer)?,
888+
event: None,
889+
})
890+
}
891+
}
892+
873893
impl<R> Type for ResponseDispatchNotifier<R>
874894
where
875895
R: Type,

0 commit comments

Comments
 (0)