1use bitwarden_api_api::models::CipherCardModel;
2use bitwarden_core::key_management::{KeyIds, SymmetricKeyId};
3use bitwarden_crypto::{CryptoError, Decryptable, EncString, Encryptable, KeyStoreContext};
4use serde::{Deserialize, Serialize};
5#[cfg(feature = "wasm")]
6use tsify_next::Tsify;
7
8use crate::VaultParseError;
9
10#[derive(Serialize, Deserialize, Debug, Clone)]
11#[serde(rename_all = "camelCase", deny_unknown_fields)]
12#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
13#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
14pub struct Card {
15 pub cardholder_name: Option<EncString>,
16 pub exp_month: Option<EncString>,
17 pub exp_year: Option<EncString>,
18 pub code: Option<EncString>,
19 pub brand: Option<EncString>,
20 pub number: Option<EncString>,
21}
22
23#[derive(Serialize, Deserialize, Debug, Clone)]
24#[serde(rename_all = "camelCase", deny_unknown_fields)]
25#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
26#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
27pub struct CardView {
28 pub cardholder_name: Option<String>,
29 pub exp_month: Option<String>,
30 pub exp_year: Option<String>,
31 pub code: Option<String>,
32 pub brand: Option<String>,
33 pub number: Option<String>,
34}
35
36#[derive(Serialize, Deserialize)]
37pub enum CardBrand {
38 Visa,
39 Mastercard,
40 Amex,
41 Discover,
42 #[serde(rename = "Diners Club")]
43 DinersClub,
44 #[serde(rename = "JCB")]
45 Jcb,
46 Maestro,
47 UnionPay,
48 RuPay,
49 #[serde(untagged)]
50 Other,
51}
52
53impl Encryptable<KeyIds, SymmetricKeyId, Card> for CardView {
54 fn encrypt(
55 &self,
56 ctx: &mut KeyStoreContext<KeyIds>,
57 key: SymmetricKeyId,
58 ) -> Result<Card, CryptoError> {
59 Ok(Card {
60 cardholder_name: self.cardholder_name.encrypt(ctx, key)?,
61 exp_month: self.exp_month.encrypt(ctx, key)?,
62 exp_year: self.exp_year.encrypt(ctx, key)?,
63 code: self.code.encrypt(ctx, key)?,
64 brand: self.brand.encrypt(ctx, key)?,
65 number: self.number.encrypt(ctx, key)?,
66 })
67 }
68}
69
70impl Decryptable<KeyIds, SymmetricKeyId, CardView> for Card {
71 fn decrypt(
72 &self,
73 ctx: &mut KeyStoreContext<KeyIds>,
74 key: SymmetricKeyId,
75 ) -> Result<CardView, CryptoError> {
76 Ok(CardView {
77 cardholder_name: self.cardholder_name.decrypt(ctx, key).ok().flatten(),
78 exp_month: self.exp_month.decrypt(ctx, key).ok().flatten(),
79 exp_year: self.exp_year.decrypt(ctx, key).ok().flatten(),
80 code: self.code.decrypt(ctx, key).ok().flatten(),
81 brand: self.brand.decrypt(ctx, key).ok().flatten(),
82 number: self.number.decrypt(ctx, key).ok().flatten(),
83 })
84 }
85}
86
87impl TryFrom<CipherCardModel> for Card {
88 type Error = VaultParseError;
89
90 fn try_from(card: CipherCardModel) -> Result<Self, Self::Error> {
91 Ok(Self {
92 cardholder_name: EncString::try_from_optional(card.cardholder_name)?,
93 exp_month: EncString::try_from_optional(card.exp_month)?,
94 exp_year: EncString::try_from_optional(card.exp_year)?,
95 code: EncString::try_from_optional(card.code)?,
96 brand: EncString::try_from_optional(card.brand)?,
97 number: EncString::try_from_optional(card.number)?,
98 })
99 }
100}