zbus/proxy/
builder.rs
1use std::{collections::HashSet, marker::PhantomData, sync::Arc};
2
3use static_assertions::assert_impl_all;
4use zbus_names::{BusName, InterfaceName};
5use zvariant::{ObjectPath, Str};
6
7use crate::{proxy::ProxyInner, Connection, Error, Proxy, Result};
8
9#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
11#[non_exhaustive]
12pub enum CacheProperties {
13 Yes,
16 No,
18 #[default]
20 Lazily,
21}
22
23#[derive(Debug)]
25pub struct Builder<'a, T = ()> {
26 conn: Connection,
27 destination: Option<BusName<'a>>,
28 path: Option<ObjectPath<'a>>,
29 interface: Option<InterfaceName<'a>>,
30 proxy_type: PhantomData<T>,
31 cache: CacheProperties,
32 uncached_properties: Option<HashSet<Str<'a>>>,
33}
34
35impl<'a, T> Clone for Builder<'a, T> {
36 fn clone(&self) -> Self {
37 Self {
38 conn: self.conn.clone(),
39 destination: self.destination.clone(),
40 path: self.path.clone(),
41 interface: self.interface.clone(),
42 cache: self.cache,
43 uncached_properties: self.uncached_properties.clone(),
44 proxy_type: PhantomData,
45 }
46 }
47}
48
49assert_impl_all!(Builder<'_>: Send, Sync, Unpin);
50
51impl<'a, T> Builder<'a, T> {
52 pub fn destination<D>(mut self, destination: D) -> Result<Self>
54 where
55 D: TryInto<BusName<'a>>,
56 D::Error: Into<Error>,
57 {
58 self.destination = Some(destination.try_into().map_err(Into::into)?);
59 Ok(self)
60 }
61
62 pub fn path<P>(mut self, path: P) -> Result<Self>
64 where
65 P: TryInto<ObjectPath<'a>>,
66 P::Error: Into<Error>,
67 {
68 self.path = Some(path.try_into().map_err(Into::into)?);
69 Ok(self)
70 }
71
72 pub fn interface<I>(mut self, interface: I) -> Result<Self>
74 where
75 I: TryInto<InterfaceName<'a>>,
76 I::Error: Into<Error>,
77 {
78 self.interface = Some(interface.try_into().map_err(Into::into)?);
79 Ok(self)
80 }
81
82 #[must_use]
84 pub fn cache_properties(mut self, cache: CacheProperties) -> Self {
85 self.cache = cache;
86 self
87 }
88
89 #[must_use]
91 pub fn uncached_properties(mut self, properties: &[&'a str]) -> Self {
92 self.uncached_properties
93 .replace(properties.iter().map(|p| Str::from(*p)).collect());
94
95 self
96 }
97
98 pub(crate) fn build_internal(self) -> Result<Proxy<'a>> {
99 let conn = self.conn;
100 let destination = self
101 .destination
102 .ok_or(Error::MissingParameter("destination"))?;
103 let path = self.path.ok_or(Error::MissingParameter("path"))?;
104 let interface = self.interface.ok_or(Error::MissingParameter("interface"))?;
105 let cache = self.cache;
106 let uncached_properties = self.uncached_properties.unwrap_or_default();
107
108 Ok(Proxy {
109 inner: Arc::new(ProxyInner::new(
110 conn,
111 destination,
112 path,
113 interface,
114 cache,
115 uncached_properties,
116 )),
117 })
118 }
119
120 pub async fn build(self) -> Result<T>
127 where
128 T: From<Proxy<'a>>,
129 {
130 let cache_upfront = self.cache == CacheProperties::Yes;
131 let proxy = self.build_internal()?;
132
133 if cache_upfront {
134 proxy
135 .get_property_cache()
136 .expect("properties cache not initialized")
137 .ready()
138 .await?;
139 }
140
141 Ok(proxy.into())
142 }
143}
144
145impl<'a, T> Builder<'a, T>
146where
147 T: ProxyDefault,
148{
149 #[must_use]
151 pub fn new(conn: &Connection) -> Self {
152 Self {
153 conn: conn.clone(),
154 destination: T::DESTINATION
155 .map(|d| BusName::from_static_str(d).expect("invalid bus name")),
156 path: T::PATH.map(|p| ObjectPath::from_static_str(p).expect("invalid default path")),
157 interface: T::INTERFACE
158 .map(|i| InterfaceName::from_static_str(i).expect("invalid interface name")),
159 cache: CacheProperties::default(),
160 uncached_properties: None,
161 proxy_type: PhantomData,
162 }
163 }
164
165 #[must_use]
167 #[deprecated(
168 since = "4.0.0",
169 note = "use `Builder::new` instead, which is now generic over the proxy type"
170 )]
171 pub fn new_bare(conn: &Connection) -> Self {
172 Self::new(conn)
173 }
174}
175
176pub trait ProxyDefault {
183 const INTERFACE: Option<&'static str>;
184 const DESTINATION: Option<&'static str>;
185 const PATH: Option<&'static str>;
186}
187
188impl ProxyDefault for Proxy<'_> {
189 const INTERFACE: Option<&'static str> = None;
190 const DESTINATION: Option<&'static str> = None;
191 const PATH: Option<&'static str> = None;
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197 use test_log::test;
198
199 #[test]
200 #[ntest::timeout(15000)]
201 fn builder() {
202 crate::utils::block_on(builder_async());
203 }
204
205 async fn builder_async() {
206 let conn = Connection::session().await.unwrap();
207
208 let builder = Builder::<Proxy<'_>>::new(&conn)
209 .destination("org.freedesktop.DBus")
210 .unwrap()
211 .path("/some/path")
212 .unwrap()
213 .interface("org.freedesktop.Interface")
214 .unwrap()
215 .cache_properties(CacheProperties::No);
216 assert!(matches!(
217 builder.clone().destination.unwrap(),
218 BusName::Unique(_),
219 ));
220 let proxy = builder.build().await.unwrap();
221 assert!(matches!(proxy.inner.destination, BusName::Unique(_)));
222 }
223}