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<'a>(zbus::Proxy<'a>);
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<'a> From<zbus::Proxy<'a>> for Collection<'a> {
28
5
    fn from(value: zbus::Proxy<'a>) -> Self {
29
        Self(value)
30
    }
31
}
32

            
33
impl<'a> Collection<'a> {
34
11
    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
37
        zbus::proxy::Builder::new(connection)
43
11
            .path(object_path)?
44
9
            .uncached_properties(&["Label", "Modified", "Items", "Locked"])
45
            .build()
46
27
            .await
47
11
            .map_err(From::from)
48
    }
49

            
50
4
    pub fn inner(&self) -> &zbus::Proxy<'_> {
51
        &self.0
52
    }
53

            
54
5
    pub(crate) async fn from_paths<P>(
55
        connection: &zbus::Connection,
56
        paths: Vec<P>,
57
    ) -> Result<Vec<Collection<'a>>, Error>
58
    where
59
        P: TryInto<ObjectPath<'a>>,
60
        P::Error: Into<zbus::Error>,
61
    {
62
9
        let mut collections = Vec::with_capacity(paths.capacity());
63
18
        for path in paths.into_iter() {
64
15
            collections.push(Self::new(connection, path).await?);
65
        }
66
4
        Ok(collections)
67
    }
68

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

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

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

            
97
16
    pub async fn items(&self) -> Result<Vec<Item<'a>>, Error> {
98
16
        let item_paths = self
99
            .inner()
100
            .get_property::<Vec<ObjectPath>>("Items")
101
16
            .await?;
102
8
        Item::from_paths(self.inner().connection(), item_paths).await
103
    }
104

            
105
16
    pub async fn label(&self) -> Result<String, Error> {
106
12
        self.inner().get_property("Label").await.map_err(From::from)
107
    }
108

            
109
16
    pub async fn set_label(&self, label: &str) -> Result<(), Error> {
110
14
        self.inner().set_property("Label", label).await?;
111
4
        Ok(())
112
    }
113

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

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

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

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

            
146
    #[doc(alias = "SearchItems")]
147
12
    pub async fn search_items(
148
        &self,
149
        attributes: &impl AsAttributes,
150
    ) -> Result<Vec<Item<'a>>, Error> {
151
48
        let msg = self
152
            .inner()
153
12
            .call_method("SearchItems", &(attributes.as_attributes()))
154
48
            .await
155
24
            .map_err::<ServiceError, _>(From::from)?;
156

            
157
24
        let item_paths = msg.body().deserialize::<Vec<OwnedObjectPath>>()?;
158
12
        Item::from_paths(self.inner().connection(), item_paths).await
159
    }
160

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

            
189
impl Serialize for Collection<'_> {
190
4
    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
191
    where
192
        S: serde::Serializer,
193
    {
194
8
        ObjectPath::serialize(self.inner().path(), serializer)
195
    }
196
}
197

            
198
impl fmt::Debug for Collection<'_> {
199
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200
        f.debug_tuple("Collection")
201
            .field(&self.inner().path().as_str())
202
            .finish()
203
    }
204
}
205

            
206
impl Unlockable for Collection<'_> {}