bitwarden_managed_settings_types/
profile.rs1use std::collections::HashMap;
2
3use bitwarden_error::bitwarden_error;
4use serde::{Deserialize, Serialize};
5use thiserror::Error;
6
7#[bitwarden_error(flat)]
9#[derive(Debug, Error)]
10pub enum ManagedSettingsError {
11 #[error("Failed to decode managed settings value: {0}")]
13 Decode(String),
14}
15
16#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
25#[serde(rename_all = "camelCase")]
26#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
27#[bitwarden_ffi::wasm_record]
28pub struct ManagementProfile {
29 pub version: u32,
31 pub updated_at: i64,
37 pub settings: HashMap<String, String>,
39}
40
41impl ManagementProfile {
42 pub fn empty() -> Self {
44 Self {
45 version: 1,
46 updated_at: 0,
47 settings: HashMap::new(),
48 }
49 }
50
51 pub fn is_managed(&self, key: &str) -> bool {
53 self.settings.contains_key(key)
54 }
55
56 pub fn get(&self, key: &str) -> Option<String> {
58 self.settings.get(key).cloned()
59 }
60
61 pub fn get_as<T: serde::de::DeserializeOwned>(
63 &self,
64 key: &str,
65 ) -> Result<Option<T>, ManagedSettingsError> {
66 match self.settings.get(key) {
67 None => Ok(None),
68 Some(raw) => serde_json::from_str(raw)
69 .map(Some)
70 .map_err(|e| ManagedSettingsError::Decode(e.to_string())),
71 }
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 fn profile_with(entries: &[(&str, &str)]) -> ManagementProfile {
80 ManagementProfile {
81 version: 1,
82 updated_at: 1_750_000_000,
83 settings: entries
84 .iter()
85 .map(|(k, v)| (k.to_string(), v.to_string()))
86 .collect(),
87 }
88 }
89
90 #[test]
91 fn is_managed_reflects_key_presence() {
92 let profile = profile_with(&[("environment.base", "\"https://vault.example.com\"")]);
93
94 assert!(profile.is_managed("environment.base"));
95 assert!(!profile.is_managed("environment.api"));
96 }
97
98 #[test]
99 fn get_returns_the_raw_json_encoded_value() {
100 let profile = profile_with(&[("environment.base", "\"https://vault.example.com\"")]);
101
102 assert_eq!(
103 profile.get("environment.base"),
104 Some("\"https://vault.example.com\"".to_string())
105 );
106 }
107
108 #[test]
109 fn get_returns_none_for_an_absent_key() {
110 let profile = profile_with(&[("environment.base", "\"https://vault.example.com\"")]);
111
112 assert_eq!(profile.get("environment.api"), None);
113 }
114
115 #[test]
116 fn get_as_decodes_a_present_value() {
117 let profile = profile_with(&[("generator.password.length", "14")]);
118
119 assert_eq!(
120 profile.get_as::<u32>("generator.password.length").unwrap(),
121 Some(14)
122 );
123 }
124
125 #[test]
126 fn get_as_returns_none_for_an_absent_key() {
127 let profile = profile_with(&[("generator.password.length", "14")]);
128
129 assert_eq!(
130 profile
131 .get_as::<u32>("generator.password.uppercase")
132 .unwrap(),
133 None
134 );
135 }
136
137 #[test]
138 fn get_as_errors_when_the_value_does_not_match_the_requested_type() {
139 let profile = profile_with(&[("generator.password.length", "\"fourteen\"")]);
140
141 let error = profile
142 .get_as::<u32>("generator.password.length")
143 .expect_err("decoding a string as u32 should fail");
144
145 assert!(matches!(error, ManagedSettingsError::Decode(_)));
146 }
147
148 #[test]
149 fn empty_profile_manages_nothing() {
150 let profile = ManagementProfile::empty();
151
152 assert!(!profile.is_managed("environment.base"));
153 assert_eq!(profile.get("environment.base"), None);
154 assert_eq!(profile.get_as::<u32>("environment.base").unwrap(), None);
155 }
156}