1
use std::{sync::Arc, time::Duration};
2

            
3
use ashpd::WindowIdentifier;
4
#[cfg(feature = "async-std")]
5
use async_lock::RwLock;
6
use futures_util::{Stream, StreamExt};
7
#[cfg(feature = "tokio")]
8
use tokio::sync::RwLock;
9
use zbus::zvariant::{ObjectPath, OwnedObjectPath};
10

            
11
use super::{Algorithm, Error, Item, api};
12
use crate::{AsAttributes, Key, Secret};
13

            
14
/// A collection allows to store and retrieve items.
15
///
16
/// The collection can be either in a locked or unlocked state, use
17
/// [`Collection::lock`] or [`Collection::unlock`] to lock or unlock it.
18
///
19
/// Using [`Collection::search_items`] or [`Collection::items`] will return no
20
/// items if the collection is locked.
21
///
22
/// **Note**
23
///
24
/// If the collection is deleted using [`Collection::delete`] any future usage
25
/// of it API will fail with [`Error::Deleted`].
26
#[derive(Debug)]
27
pub struct Collection {
28
    inner: Arc<api::Collection>,
29
    service: Arc<api::Service>,
30
    session: Arc<api::Session>,
31
    algorithm: Algorithm,
32
    /// Defines whether the Collection has been deleted or not
33
    available: RwLock<bool>,
34
    aes_key: Option<Arc<Key>>,
35
}
36

            
37
impl Collection {
38
23
    pub(crate) fn new(
39
        service: Arc<api::Service>,
40
        session: Arc<api::Session>,
41
        algorithm: Algorithm,
42
        collection: api::Collection,
43
        aes_key: Option<Arc<Key>>,
44
    ) -> Self {
45
        Self {
46
40
            inner: Arc::new(collection),
47
            session,
48
            service,
49
            algorithm,
50
19
            available: RwLock::new(true),
51
            aes_key,
52
        }
53
    }
54

            
55
55
    pub(crate) async fn is_available(&self) -> bool {
56
30
        *self.available.read().await
57
    }
58

            
59
    /// Retrieve the list of available [`Item`] in the collection.
60
20
    pub async fn items(&self) -> Result<Vec<Item>, Error> {
61
14
        if !self.is_available().await {
62
2
            Err(Error::Deleted)
63
        } else {
64
20
            Ok(self
65
                .inner
66
4
                .items()
67
16
                .await?
68
4
                .into_iter()
69
12
                .map(|item| self.new_item(item))
70
4
                .collect::<Vec<_>>())
71
        }
72
    }
73

            
74
    /// The collection label.
75
20
    pub async fn label(&self) -> Result<String, Error> {
76
10
        if !self.is_available().await {
77
2
            Err(Error::Deleted)
78
        } else {
79
12
            self.inner.label().await
80
        }
81
    }
82

            
83
    /// Set the collection label.
84
20
    pub async fn set_label(&self, label: &str) -> Result<(), Error> {
85
10
        if !self.is_available().await {
86
2
            Err(Error::Deleted)
87
        } else {
88
12
            self.inner.set_label(label).await
89
        }
90
    }
91

            
92
    /// Get whether the collection is locked.
93
    #[doc(alias = "Locked")]
94
20
    pub async fn is_locked(&self) -> Result<bool, Error> {
95
10
        if !self.is_available().await {
96
2
            Err(Error::Deleted)
97
        } else {
98
12
            self.inner.is_locked().await
99
        }
100
    }
101

            
102
    /// The UNIX time when the collection was created.
103
10
    pub async fn created(&self) -> Result<Duration, Error> {
104
6
        if !self.is_available().await {
105
2
            Err(Error::Deleted)
106
        } else {
107
6
            self.inner.created().await
108
        }
109
    }
110

            
111
    /// The UNIX time when the collection was modified.
112
10
    pub async fn modified(&self) -> Result<Duration, Error> {
113
6
        if !self.is_available().await {
114
2
            Err(Error::Deleted)
115
        } else {
116
6
            self.inner.modified().await
117
        }
118
    }
119

            
120
    /// Search for items based on their attributes.
121
80
    pub async fn search_items(&self, attributes: &impl AsAttributes) -> Result<Vec<Item>, Error> {
122
50
        if !self.is_available().await {
123
2
            Err(Error::Deleted)
124
        } else {
125
49
            let items = self.inner.search_items(attributes).await?;
126
32
            Ok(items
127
16
                .into_iter()
128
48
                .map(|item| self.new_item(item))
129
16
                .collect::<Vec<_>>())
130
        }
131
    }
132

            
133
    /// Create a new item on the collection
134
    ///
135
    /// # Arguments
136
    ///
137
    /// * `label` - A user visible label of the item.
138
    /// * `attributes` - A map of key/value attributes, used to find the item
139
    ///   later.
140
    /// * `secret` - The secret to store.
141
    /// * `replace` - Whether to replace the value if the `attributes` matches
142
    ///   an existing `secret`.
143
21
    pub async fn create_item(
144
        &self,
145
        label: &str,
146
        attributes: &impl AsAttributes,
147
        secret: impl Into<Secret>,
148
        replace: bool,
149
        window_id: Option<WindowIdentifier>,
150
    ) -> Result<Item, Error> {
151
47
        if !self.is_available().await {
152
2
            Err(Error::Deleted)
153
        } else {
154
22
            let secret = match self.algorithm {
155
13
                Algorithm::Plain => api::DBusSecret::new(Arc::clone(&self.session), secret),
156
                Algorithm::Encrypted => api::DBusSecret::new_encrypted(
157
28
                    Arc::clone(&self.session),
158
20
                    secret,
159
33
                    self.aes_key.as_ref().unwrap(),
160
                )?,
161
            };
162
87
            let item = self
163
                .inner
164
21
                .create_item(label, attributes, &secret, replace, window_id)
165
88
                .await?;
166

            
167
43
            Ok(self.new_item(item))
168
        }
169
    }
170

            
171
    /// Unlock the collection.
172
20
    pub async fn unlock(&self, window_id: Option<WindowIdentifier>) -> Result<(), Error> {
173
14
        if !self.is_available().await {
174
2
            Err(Error::Deleted)
175
        } else {
176
16
            self.service
177
4
                .unlock(&[self.inner.inner().path()], window_id)
178
20
                .await?;
179
4
            Ok(())
180
        }
181
    }
182

            
183
    /// Lock the collection.
184
20
    pub async fn lock(&self, window_id: Option<WindowIdentifier>) -> Result<(), Error> {
185
14
        if !self.is_available().await {
186
2
            Err(Error::Deleted)
187
        } else {
188
16
            self.service
189
4
                .lock(&[self.inner.inner().path()], window_id)
190
20
                .await?;
191
4
            Ok(())
192
        }
193
    }
194

            
195
    /// Delete the collection.
196
20
    pub async fn delete(&self, window_id: Option<WindowIdentifier>) -> Result<(), Error> {
197
15
        if !self.is_available().await {
198
2
            Err(Error::Deleted)
199
        } else {
200
13
            self.inner.delete(window_id).await?;
201
6
            *self.available.write().await = false;
202
5
            Ok(())
203
        }
204
    }
205

            
206
    /// Returns collection path
207
2
    pub fn path(&self) -> &ObjectPath<'_> {
208
2
        self.inner.inner().path()
209
    }
210

            
211
    /// Stream yielding when new items get created
212
16
    pub async fn receive_item_created(&self) -> Result<impl Stream<Item = Item> + '_, Error> {
213
24
        Ok(self
214
            .inner
215
4
            .receive_item_created()
216
16
            .await?
217
16
            .map(|item| self.new_item(item)))
218
    }
219

            
220
    /// Stream yielding when existing items get changed
221
16
    pub async fn receive_item_changed(&self) -> Result<impl Stream<Item = Item> + '_, Error> {
222
24
        Ok(self
223
            .inner
224
4
            .receive_item_changed()
225
16
            .await?
226
16
            .map(|item| self.new_item(item)))
227
    }
228

            
229
    /// Stream yielding when existing items get deleted
230
16
    pub async fn receive_item_deleted(&self) -> Result<impl Stream<Item = OwnedObjectPath>, Error> {
231
12
        self.inner.receive_item_deleted().await
232
    }
233

            
234
    // Get public `Item`` from `api::Item`
235
10
    fn new_item(&self, item: api::Item) -> Item {
236
        Item::new(
237
23
            Arc::clone(&self.service),
238
24
            Arc::clone(&self.session),
239
15
            self.algorithm,
240
11
            item,
241
15
            self.aes_key.clone(), // Cheap clone, it is an Arc,
242
        )
243
    }
244
}