1use bitwarden_vault::FieldType;
2use credential_exchange_format::{
3 EditableField, EditableFieldBoolean, EditableFieldConcealedString, EditableFieldCountryCode,
4 EditableFieldDate, EditableFieldString, EditableFieldWifiNetworkSecurityType,
5 EditableFieldYearMonth,
6};
7
8use crate::Field;
9
10pub(super) fn create_field<T>(name: impl Into<String>, field: &T) -> Field
12where
13 T: EditableFieldToField,
14{
15 Field {
16 name: Some(name.into()),
17 value: Some(field.field_value()),
18 r#type: T::FIELD_TYPE as u8,
19 linked_id: None,
20 }
21}
22
23pub(super) trait EditableFieldToField {
25 const FIELD_TYPE: FieldType;
26
27 fn field_value(&self) -> String;
28}
29
30impl EditableFieldToField for EditableField<EditableFieldString> {
31 const FIELD_TYPE: FieldType = FieldType::Text;
32
33 fn field_value(&self) -> String {
34 self.value.0.clone()
35 }
36}
37
38impl EditableFieldToField for EditableField<EditableFieldConcealedString> {
39 const FIELD_TYPE: FieldType = FieldType::Hidden;
40
41 fn field_value(&self) -> String {
42 self.value.0.clone()
43 }
44}
45
46impl EditableFieldToField for EditableField<EditableFieldBoolean> {
47 const FIELD_TYPE: FieldType = FieldType::Boolean;
48
49 fn field_value(&self) -> String {
50 self.value.0.to_string()
51 }
52}
53
54impl EditableFieldToField for EditableField<EditableFieldWifiNetworkSecurityType> {
55 const FIELD_TYPE: FieldType = FieldType::Text;
56
57 fn field_value(&self) -> String {
58 security_type_to_string(&self.value).to_string()
59 }
60}
61
62impl EditableFieldToField for EditableField<EditableFieldCountryCode> {
63 const FIELD_TYPE: FieldType = FieldType::Text;
64
65 fn field_value(&self) -> String {
66 self.value.0.clone()
67 }
68}
69
70impl EditableFieldToField for EditableField<EditableFieldDate> {
71 const FIELD_TYPE: FieldType = FieldType::Text;
72
73 fn field_value(&self) -> String {
74 self.value.0.to_string()
75 }
76}
77
78impl EditableFieldToField for EditableField<EditableFieldYearMonth> {
79 const FIELD_TYPE: FieldType = FieldType::Text;
80
81 fn field_value(&self) -> String {
82 format!(
83 "{:04}-{:02}",
84 self.value.year,
85 self.value.month.number_from_month()
86 )
87 }
88}
89
90fn security_type_to_string(security_type: &EditableFieldWifiNetworkSecurityType) -> &str {
92 use EditableFieldWifiNetworkSecurityType::*;
93 match security_type {
94 Unsecured => "Unsecured",
95 WpaPersonal => "WPA Personal",
96 Wpa2Personal => "WPA2 Personal",
97 Wpa3Personal => "WPA3 Personal",
98 Wep => "WEP",
99 Other(s) => s,
100 _ => "Unknown",
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_create_field_string() {
110 let editable_field = EditableField {
111 id: None,
112 label: None,
113 value: EditableFieldString("Test Value".to_string()),
114 extensions: None,
115 };
116
117 let field = create_field("Test Name", &editable_field);
118
119 assert_eq!(
120 field,
121 Field {
122 name: Some("Test Name".to_string()),
123 value: Some("Test Value".to_string()),
124 r#type: FieldType::Text as u8,
125 linked_id: None,
126 }
127 );
128 }
129
130 #[test]
131 fn test_create_field_concealed_string() {
132 let editable_field = EditableField {
133 id: None,
134 label: None,
135 value: EditableFieldConcealedString("Secret123".to_string()),
136 extensions: None,
137 };
138
139 let field = create_field("Password", &editable_field);
140
141 assert_eq!(
142 field,
143 Field {
144 name: Some("Password".to_string()),
145 value: Some("Secret123".to_string()),
146 r#type: FieldType::Hidden as u8,
147 linked_id: None,
148 }
149 );
150 }
151
152 #[test]
153 fn test_create_field_boolean_true() {
154 let editable_field = EditableField {
155 id: None,
156 label: None,
157 value: EditableFieldBoolean(true),
158 extensions: None,
159 };
160
161 let field = create_field("Is Enabled", &editable_field);
162
163 assert_eq!(
164 field,
165 Field {
166 name: Some("Is Enabled".to_string()),
167 value: Some("true".to_string()),
168 r#type: FieldType::Boolean as u8,
169 linked_id: None,
170 }
171 );
172 }
173
174 #[test]
175 fn test_create_field_boolean_false() {
176 let editable_field = EditableField {
177 id: None,
178 label: None,
179 value: EditableFieldBoolean(false),
180 extensions: None,
181 };
182
183 let field = create_field("Is Hidden", &editable_field);
184
185 assert_eq!(
186 field,
187 Field {
188 name: Some("Is Hidden".to_string()),
189 value: Some("false".to_string()),
190 r#type: FieldType::Boolean as u8,
191 linked_id: None,
192 }
193 );
194 }
195
196 #[test]
197 fn test_create_field_wifi_security() {
198 let editable_field = EditableField {
199 id: None,
200 label: None,
201 value: EditableFieldWifiNetworkSecurityType::Wpa3Personal,
202 extensions: None,
203 };
204
205 let field = create_field("WiFi Security", &editable_field);
206
207 assert_eq!(
208 field,
209 Field {
210 name: Some("WiFi Security".to_string()),
211 value: Some("WPA3 Personal".to_string()),
212 r#type: FieldType::Text as u8,
213 linked_id: None,
214 }
215 );
216 }
217
218 #[test]
219 fn test_security_type_to_string() {
220 assert_eq!(
221 security_type_to_string(&EditableFieldWifiNetworkSecurityType::Unsecured),
222 "Unsecured"
223 );
224 assert_eq!(
225 security_type_to_string(&EditableFieldWifiNetworkSecurityType::WpaPersonal),
226 "WPA Personal"
227 );
228 assert_eq!(
229 security_type_to_string(&EditableFieldWifiNetworkSecurityType::Wpa2Personal),
230 "WPA2 Personal"
231 );
232 assert_eq!(
233 security_type_to_string(&EditableFieldWifiNetworkSecurityType::Wpa3Personal),
234 "WPA3 Personal"
235 );
236 assert_eq!(
237 security_type_to_string(&EditableFieldWifiNetworkSecurityType::Wep),
238 "WEP"
239 );
240
241 let custom_security = "WPA2 Enterprise";
242 assert_eq!(
243 security_type_to_string(&EditableFieldWifiNetworkSecurityType::Other(
244 custom_security.to_string()
245 )),
246 custom_security
247 );
248 }
249
250 #[test]
251 fn test_create_field_date() {
252 use chrono::NaiveDate;
253
254 let editable_field = EditableField {
255 id: None,
256 label: None,
257 value: EditableFieldDate(NaiveDate::from_ymd_opt(2025, 1, 15).unwrap()),
258 extensions: None,
259 };
260
261 let field = create_field("Expiry Date".to_string(), &editable_field);
262
263 assert_eq!(
264 field,
265 Field {
266 name: Some("Expiry Date".to_string()),
267 value: Some("2025-01-15".to_string()),
268 r#type: FieldType::Text as u8,
269 linked_id: None,
270 }
271 );
272 }
273
274 #[test]
275 fn test_create_field_year_month() {
276 use chrono::Month;
277
278 let editable_field = EditableField {
279 id: None,
280 label: None,
281 value: EditableFieldYearMonth {
282 year: 2025,
283 month: Month::December,
284 },
285 extensions: None,
286 };
287
288 let field = create_field("Card Expiry", &editable_field);
289
290 assert_eq!(
291 field,
292 Field {
293 name: Some("Card Expiry".to_string()),
294 value: Some("2025-12".to_string()),
295 r#type: FieldType::Text as u8,
296 linked_id: None,
297 }
298 );
299 }
300}