oo7/dbus/api/
collection.rs

1use 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<'_> {}