bitwarden_collections/
collection.rs1use 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}