1
use std::{fmt, time::Duration};
2

            
3
use ashpd::WindowIdentifier;
4
use futures_util::{Stream, StreamExt};
5
use serde::Serialize;
6
use zbus::zvariant::{ObjectPath, OwnedObjectPath, Type};
7

            
8
use super::{DBusSecret, DESTINATION, Item, Prompt, Properties, Unlockable};
9
use crate::{
10
    AsAttributes,
11
    dbus::{Error, ServiceError},
12
};
13

            
14
#[derive(Type)]
15
#[zvariant(signature = "o")]
16
#[doc(alias = "org.freedesktop.Secret.Collection")]
17
pub struct Collection(zbus::Proxy<'static>);
18

            
19
impl 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

            
27
impl From<zbus::Proxy<'static>> for Collection {
28
2
    fn from(value: zbus::Proxy<'static>) -> Self {
29
        Self(value)
30
    }
31
}
32

            
33
impl Collection {
34
14
    pub async fn new<P>(connection: &zbus::Connection, object_path: P) -> Result<Self, Error>
35
    where
36
        P: TryInto<ObjectPath<'static>>,
37
        P::Error: Into<zbus::Error>,
38
    {
39
30
        zbus::proxy::Builder::new(connection)
40
6
            .path(object_path)?
41
6
            .uncached_properties(&["Label", "Modified", "Items", "Locked"])
42
            .build()
43
22
            .await
44
8
            .map_err(From::from)
45
    }
46

            
47
2
    pub fn inner(&self) -> &zbus::Proxy<'static> {
48
        &self.0
49
    }
50

            
51
4
    pub(crate) async fn from_paths<P>(
52
        connection: &zbus::Connection,
53
        paths: Vec<P>,
54
    ) -> Result<Vec<Self>, Error>
55
    where
56
        P: TryInto<ObjectPath<'static>>,
57
        P::Error: Into<zbus::Error>,
58
    {
59
6
        let mut collections = Vec::with_capacity(paths.capacity());
60
12
        for path in paths.into_iter() {
61
11
            collections.push(Self::new(connection, path).await?);
62
        }
63
4
        Ok(collections)
64
    }
65

            
66
    #[doc(alias = "ItemCreated")]
67
8
    pub async fn receive_item_created(&self) -> Result<impl Stream<Item = Item> + '_, Error> {
68
6
        let stream = self.inner().receive_signal("ItemCreated").await?;
69
4
        let conn = self.inner().connection();
70
10
        Ok(stream.filter_map(move |message| async move {
71
4
            let path = message.body().deserialize::<OwnedObjectPath>().ok()?;
72
2
            Item::new(conn, path).await.ok()
73
        }))
74
    }
75

            
76
    #[doc(alias = "ItemDeleted")]
77
8
    pub async fn receive_item_deleted(&self) -> Result<impl Stream<Item = OwnedObjectPath>, Error> {
78
6
        let stream = self.inner().receive_signal("ItemDeleted").await?;
79
12
        Ok(stream.filter_map(move |message| async move {
80
4
            message.body().deserialize::<OwnedObjectPath>().ok()
81
        }))
82
    }
83

            
84
    #[doc(alias = "ItemChanged")]
85
8
    pub async fn receive_item_changed(&self) -> Result<impl Stream<Item = Item> + '_, Error> {
86
6
        let stream = self.inner().receive_signal("ItemChanged").await?;
87
4
        let conn = self.inner().connection();
88
10
        Ok(stream.filter_map(move |message| async move {
89
4
            let path = message.body().deserialize::<OwnedObjectPath>().ok()?;
90
2
            Item::new(conn, path).await.ok()
91
        }))
92
    }
93

            
94
8
    pub async fn items(&self) -> Result<Vec<Item>, Error> {
95
8
        let item_paths = self
96
            .inner()
97
            .get_property::<Vec<ObjectPath>>("Items")
98
8
            .await?;
99
4
        Item::from_paths(self.inner().connection(), item_paths).await
100
    }
101

            
102
8
    pub async fn label(&self) -> Result<String, Error> {
103
6
        self.inner().get_property("Label").await.map_err(From::from)
104
    }
105

            
106
8
    pub async fn set_label(&self, label: &str) -> Result<(), Error> {
107
8
        self.inner().set_property("Label", label).await?;
108
2
        Ok(())
109
    }
110

            
111
    #[doc(alias = "Locked")]
112
8
    pub async fn is_locked(&self) -> Result<bool, Error> {
113
6
        self.inner()
114
            .get_property("Locked")
115
8
            .await
116
2
            .map_err(From::from)
117
    }
118

            
119
8
    pub async fn created(&self) -> Result<Duration, Error> {
120
6
        let time = self.inner().get_property::<u64>("Created").await?;
121
2
        Ok(Duration::from_secs(time))
122
    }
123

            
124
8
    pub async fn modified(&self) -> Result<Duration, Error> {
125
6
        let time = self.inner().get_property::<u64>("Modified").await?;
126
2
        Ok(Duration::from_secs(time))
127
    }
128

            
129
10
    pub async fn delete(&self, window_id: Option<WindowIdentifier>) -> Result<(), Error> {
130
10
        let prompt_path = self
131
            .inner()
132
2
            .call_method("Delete", &())
133
8
            .await
134
2
            .map_err::<ServiceError, _>(From::from)?
135
            .body()
136
            .deserialize::<OwnedObjectPath>()?;
137
2
        if let Some(prompt) = Prompt::new(self.inner().connection(), prompt_path).await? {
138
6
            let _ = prompt.receive_completed(window_id).await?;
139
        }
140
2
        Ok(())
141
    }
142

            
143
    #[doc(alias = "SearchItems")]
144
24
    pub async fn search_items(&self, attributes: &impl AsAttributes) -> Result<Vec<Item>, Error> {
145
24
        let msg = self
146
            .inner()
147
6
            .call_method("SearchItems", &(attributes.as_attributes()))
148
24
            .await
149
12
            .map_err::<ServiceError, _>(From::from)?;
150

            
151
12
        let item_paths = msg.body().deserialize::<Vec<OwnedObjectPath>>()?;
152
6
        Item::from_paths(self.inner().connection(), item_paths).await
153
    }
154

            
155
    #[doc(alias = "CreateItem")]
156
4
    pub async fn create_item(
157
        &self,
158
        label: &str,
159
        attributes: &impl AsAttributes,
160
        secret: &DBusSecret,
161
        replace: bool,
162
        window_id: Option<WindowIdentifier>,
163
    ) -> Result<Item, Error> {
164
8
        let properties = Properties::for_item(label, attributes);
165
24
        let (item_path, prompt_path) = self
166
            .inner()
167
4
            .call_method("CreateItem", &(properties, secret, replace))
168
16
            .await
169
6
            .map_err::<ServiceError, _>(From::from)?
170
            .body()
171
            .deserialize::<(OwnedObjectPath, OwnedObjectPath)>()?;
172
4
        let cnx = self.inner().connection();
173
12
        let item_path = if let Some(prompt) = Prompt::new(cnx, prompt_path).await? {
174
6
            let response = prompt.receive_completed(window_id).await?;
175
4
            OwnedObjectPath::try_from(response)?
176
        } else {
177
4
            item_path
178
        };
179
8
        Item::new(self.inner().connection(), item_path).await
180
    }
181
}
182

            
183
impl Serialize for Collection {
184
4
    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
185
    where
186
        S: serde::Serializer,
187
    {
188
8
        ObjectPath::serialize(self.inner().path(), serializer)
189
    }
190
}
191

            
192
impl fmt::Debug for Collection {
193
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194
        f.debug_tuple("Collection")
195
            .field(&self.inner().path().as_str())
196
            .finish()
197
    }
198
}
199

            
200
impl Unlockable for Collection {}