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
    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
            inner: Arc::new(collection),
47
            session,
48
            service,
49
            algorithm,
50
            available: RwLock::new(true),
51
            aes_key,
52
        }
53
    }
54

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

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

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

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

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

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

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

            
120
    /// Search for items based on their attributes.
121
    pub async fn search_items(&self, attributes: &impl AsAttributes) -> Result<Vec<Item>, Error> {
122
        if !self.is_available().await {
123
            Err(Error::Deleted)
124
        } else {
125
            let items = self.inner.search_items(attributes).await?;
126
            Ok(items
127
                .into_iter()
128
                .map(|item| self.new_item(item))
129
                .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
    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
        if !self.is_available().await {
152
            Err(Error::Deleted)
153
        } else {
154
            let secret = match self.algorithm {
155
                Algorithm::Plain => api::DBusSecret::new(Arc::clone(&self.session), secret),
156
                Algorithm::Encrypted => api::DBusSecret::new_encrypted(
157
                    Arc::clone(&self.session),
158
                    secret,
159
                    self.aes_key.as_ref().unwrap(),
160
                )?,
161
            };
162
            let item = self
163
                .inner
164
                .create_item(label, attributes, &secret, replace, window_id)
165
                .await?;
166

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

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

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

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

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

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

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

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

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