oo7/dbus/api/
collection.rs1use std::{fmt, time::Duration};
2
3use ashpd::WindowIdentifier;
4use futures_util::{Stream, StreamExt};
5use serde::Serialize;
6use zbus::zvariant::{ObjectPath, OwnedObjectPath, Type};
7
8use super::{DBusSecret, DESTINATION, Item, Prompt, Properties, Unlockable};
9use crate::{
10 AsAttributes,
11 dbus::{Error, ServiceError},
12};
13
14#[derive(Type)]
15#[zvariant(signature = "o")]
16#[doc(alias = "org.freedesktop.Secret.Collection")]
17pub struct Collection<'a>(zbus::Proxy<'a>);
18
19impl zbus::proxy::Defaults for Collection<'_> {
20 const INTERFACE: &'static Option<zbus::names::InterfaceName<'static>> = &Some(
21 zbus::names::InterfaceName::from_static_str_unchecked("org.freedesktop.Secret.Collection"),
22 );
23 const DESTINATION: &'static Option<zbus::names::BusName<'static>> = &Some(DESTINATION);
24 const PATH: &'static Option<ObjectPath<'static>> = &None;
25}
26
27impl<'a> From<zbus::Proxy<'a>> for Collection<'a> {
28 fn from(value: zbus::Proxy<'a>) -> Self {
29 Self(value)
30 }
31}
32
33impl<'a> Collection<'a> {
34 pub async fn new<P>(
35 connection: &zbus::Connection,
36 object_path: P,
37 ) -> Result<Collection<'a>, Error>
38 where
39 P: TryInto<ObjectPath<'a>>,
40 P::Error: Into<zbus::Error>,
41 {
42 zbus::proxy::Builder::new(connection)
43 .path(object_path)?
44 .build()
45 .await
46 .map_err(From::from)
47 }
48
49 pub fn inner(&self) -> &zbus::Proxy<'_> {
50 &self.0
51 }
52
53 pub(crate) async fn from_paths<P>(
54 connection: &zbus::Connection,
55 paths: Vec<P>,
56 ) -> Result<Vec<Collection<'a>>, Error>
57 where
58 P: TryInto<ObjectPath<'a>>,
59 P::Error: Into<zbus::Error>,
60 {
61 let mut collections = Vec::with_capacity(paths.capacity());
62 for path in paths.into_iter() {
63 collections.push(Self::new(connection, path).await?);
64 }
65 Ok(collections)
66 }
67
68 #[doc(alias = "ItemCreated")]
69 pub async fn receive_item_created(&self) -> Result<impl Stream<Item = Item<'a>> + '_, Error> {
70 let stream = self.inner().receive_signal("ItemCreated").await?;
71 let conn = self.inner().connection();
72 Ok(stream.filter_map(move |message| async move {
73 let path = message.body().deserialize::<OwnedObjectPath>().ok()?;
74 Item::new(conn, path).await.ok()
75 }))
76 }
77
78 #[doc(alias = "ItemDeleted")]
79 pub async fn receive_item_deleted(&self) -> Result<impl Stream<Item = OwnedObjectPath>, Error> {
80 let stream = self.inner().receive_signal("ItemDeleted").await?;
81 Ok(stream.filter_map(move |message| async move {
82 message.body().deserialize::<OwnedObjectPath>().ok()
83 }))
84 }
85
86 #[doc(alias = "ItemChanged")]
87 pub async fn receive_item_changed(&self) -> Result<impl Stream<Item = Item<'a>> + '_, Error> {
88 let stream = self.inner().receive_signal("ItemChanged").await?;
89 let conn = self.inner().connection();
90 Ok(stream.filter_map(move |message| async move {
91 let path = message.body().deserialize::<OwnedObjectPath>().ok()?;
92 Item::new(conn, path).await.ok()
93 }))
94 }
95
96 pub async fn items(&self) -> Result<Vec<Item<'a>>, Error> {
97 let item_paths = self
98 .inner()
99 .get_property::<Vec<ObjectPath>>("Items")
100 .await?;
101 Item::from_paths(self.inner().connection(), item_paths).await
102 }
103
104 pub async fn label(&self) -> Result<String, Error> {
105 self.inner().get_property("Label").await.map_err(From::from)
106 }
107
108 pub async fn set_label(&self, label: &str) -> Result<(), Error> {
109 self.inner().set_property("Label", label).await?;
110 Ok(())
111 }
112
113 #[doc(alias = "Locked")]
114 pub async fn is_locked(&self) -> Result<bool, Error> {
115 self.inner()
116 .get_property("Locked")
117 .await
118 .map_err(From::from)
119 }
120
121 pub async fn created(&self) -> Result<Duration, Error> {
122 let time = self.inner().get_property::<u64>("Created").await?;
123 Ok(Duration::from_secs(time))
124 }
125
126 pub async fn modified(&self) -> Result<Duration, Error> {
127 let time = self.inner().get_property::<u64>("Modified").await?;
128 Ok(Duration::from_secs(time))
129 }
130
131 pub async fn delete(&self, window_id: Option<WindowIdentifier>) -> Result<(), Error> {
132 let prompt_path = self
133 .inner()
134 .call_method("Delete", &())
135 .await
136 .map_err::<ServiceError, _>(From::from)?
137 .body()
138 .deserialize::<OwnedObjectPath>()?;
139 if let Some(prompt) = Prompt::new(self.inner().connection(), prompt_path).await? {
140 let _ = prompt.receive_completed(window_id).await?;
141 }
142 Ok(())
143 }
144
145 #[doc(alias = "SearchItems")]
146 pub async fn search_items(
147 &self,
148 attributes: &impl AsAttributes,
149 ) -> Result<Vec<Item<'a>>, Error> {
150 let msg = self
151 .inner()
152 .call_method("SearchItems", &(attributes.as_attributes()))
153 .await
154 .map_err::<ServiceError, _>(From::from)?;
155
156 let item_paths = msg.body().deserialize::<Vec<OwnedObjectPath>>()?;
157 Item::from_paths(self.inner().connection(), item_paths).await
158 }
159
160 #[doc(alias = "CreateItem")]
161 pub async fn create_item(
162 &self,
163 label: &str,
164 attributes: &impl AsAttributes,
165 secret: &DBusSecret<'_>,
166 replace: bool,
167 window_id: Option<WindowIdentifier>,
168 ) -> Result<Item<'a>, Error> {
169 let properties = Properties::for_item(label, attributes);
170 let (item_path, prompt_path) = self
171 .inner()
172 .call_method("CreateItem", &(properties, secret, replace))
173 .await
174 .map_err::<ServiceError, _>(From::from)?
175 .body()
176 .deserialize::<(OwnedObjectPath, OwnedObjectPath)>()?;
177 let cnx = self.inner().connection();
178 let item_path = if let Some(prompt) = Prompt::new(cnx, prompt_path).await? {
179 let response = prompt.receive_completed(window_id).await?;
180 OwnedObjectPath::try_from(response)?
181 } else {
182 item_path
183 };
184 Item::new(self.inner().connection(), item_path).await
185 }
186}
187
188impl Serialize for Collection<'_> {
189 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
190 where
191 S: serde::Serializer,
192 {
193 ObjectPath::serialize(self.inner().path(), serializer)
194 }
195}
196
197impl fmt::Debug for Collection<'_> {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 f.debug_tuple("Collection")
200 .field(&self.inner().path().as_str())
201 .finish()
202 }
203}
204
205impl Unlockable for Collection<'_> {}