bitwarden_collections/
collection.rs

1use bitwarden_api_api::models::CollectionDetailsResponseModel;
2use bitwarden_core::{
3    key_management::{KeyIds, SymmetricKeyId},
4    require,
5};
6use bitwarden_crypto::{CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext};
7use serde::{Deserialize, Serialize};
8use uuid::Uuid;
9#[cfg(feature = "wasm")]
10use {tsify::Tsify, wasm_bindgen::prelude::*};
11
12use crate::{error::CollectionsParseError, tree::TreeItem};
13
14#[allow(missing_docs)]
15#[derive(Serialize, Deserialize, Debug)]
16#[serde(rename_all = "camelCase", deny_unknown_fields)]
17#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
18#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
19pub struct Collection {
20    pub id: Option<Uuid>,
21    pub organization_id: Uuid,
22    pub name: EncString,
23    pub external_id: Option<String>,
24    pub hide_passwords: bool,
25    pub read_only: bool,
26    pub manage: bool,
27}
28
29#[allow(missing_docs)]
30#[derive(Serialize, Deserialize, Debug, Clone)]
31#[serde(rename_all = "camelCase", deny_unknown_fields)]
32#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
33#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
34pub struct CollectionView {
35    pub id: Option<Uuid>,
36    pub organization_id: Uuid,
37    pub name: String,
38    pub external_id: Option<String>,
39    pub hide_passwords: bool,
40    pub read_only: bool,
41    pub manage: bool,
42}
43
44#[allow(missing_docs)]
45impl Decryptable<KeyIds, SymmetricKeyId, CollectionView> for Collection {
46    fn decrypt(
47        &self,
48        ctx: &mut KeyStoreContext<KeyIds>,
49        key: SymmetricKeyId,
50    ) -> Result<CollectionView, CryptoError> {
51        Ok(CollectionView {
52            id: self.id,
53            organization_id: self.organization_id,
54            name: self.name.decrypt(ctx, key).ok().unwrap_or_default(),
55            external_id: self.external_id.clone(),
56            hide_passwords: self.hide_passwords,
57            read_only: self.read_only,
58            manage: self.manage,
59        })
60    }
61}
62
63#[allow(missing_docs)]
64impl TryFrom<CollectionDetailsResponseModel> for Collection {
65    type Error = CollectionsParseError;
66
67    fn try_from(collection: CollectionDetailsResponseModel) -> Result<Self, Self::Error> {
68        Ok(Collection {
69            id: collection.id,
70            organization_id: require!(collection.organization_id),
71            name: require!(collection.name).parse()?,
72            external_id: collection.external_id,
73            hide_passwords: collection.hide_passwords.unwrap_or(false),
74            read_only: collection.read_only.unwrap_or(false),
75            manage: collection.manage.unwrap_or(false),
76        })
77    }
78}
79
80#[allow(missing_docs)]
81impl IdentifyKey<SymmetricKeyId> for Collection {
82    fn key_identifier(&self) -> SymmetricKeyId {
83        SymmetricKeyId::Organization(self.organization_id)
84    }
85}
86
87#[allow(missing_docs)]
88impl TreeItem for CollectionView {
89    fn id(&self) -> Uuid {
90        self.id.unwrap_or_default()
91    }
92
93    fn short_name(&self) -> &str {
94        self.path().last().unwrap_or(&"")
95    }
96
97    fn path(&self) -> Vec<&str> {
98        self.name
99            .split(Self::DELIMITER)
100            .filter(|s| !s.is_empty())
101            .collect::<Vec<&str>>()
102    }
103
104    const DELIMITER: char = '/';
105}