1use super::association::Association;
10use super::binary_key::BinaryKey;
11use super::binary_value::BinaryValue;
12use super::color::Color;
13use super::custom_icon_uuid::CustomIconUuid;
14use super::entry_uuid::EntryUuid;
15use super::icon::Icon;
16use super::obfuscation::Obfuscation;
17use super::string_key::StringKey;
18use super::string_value::StringValue;
19use super::strings_map::StringsMap;
20use super::times::Times;
21use crate::{common, GroupUuid};
22use chrono::{DateTime, Utc};
23use std::collections::HashMap;
24use std::str;
25
26#[derive(Clone, Debug, PartialEq)]
28pub struct Entry {
29 pub associations: Vec<Association>,
31
32 pub auto_type_def_sequence: String,
34
35 pub auto_type_enabled: bool,
37
38 pub auto_type_obfuscation: Obfuscation,
40
41 pub background_color: Option<Color>,
43
44 pub binaries: HashMap<BinaryKey, BinaryValue>,
46
47 pub creation_time: DateTime<Utc>,
49
50 pub custom_icon_uuid: Option<CustomIconUuid>,
52
53 pub expires: bool,
55
56 pub expiry_time: DateTime<Utc>,
58
59 pub foreground_color: Option<Color>,
61
62 pub history: Vec<Entry>,
64
65 pub icon: Icon,
67
68 pub last_accessed: DateTime<Utc>,
70
71 pub last_modified: DateTime<Utc>,
73
74 pub location_changed: DateTime<Utc>,
76
77 pub override_url: String,
79
80 pub strings: StringsMap,
82
83 pub tags: String,
85
86 pub usage_count: i32,
88
89 pub uuid: EntryUuid,
91
92 pub parent: GroupUuid,
94}
95
96impl Entry {
97 pub fn new() -> Entry {
99 let mut entry = Entry::default();
100 entry.uuid = EntryUuid::new_random();
101 entry
102 }
103
104 pub fn notes(&self) -> Option<&str> {
106 self.other(StringKey::Notes)
107 }
108
109 pub fn other(&self, key: StringKey) -> Option<&str> {
111 match self.strings.get(&key) {
112 Some(&StringValue::Plain(ref string)) => Some(string),
113 Some(&StringValue::Protected(ref secstr)) => str::from_utf8(secstr.unsecure()).ok(),
114 None => None,
115 }
116 }
117
118 pub fn password(&self) -> Option<&str> {
120 self.other(StringKey::Password)
121 }
122
123 pub fn set_notes<S: Into<String>>(&mut self, val: S) {
125 self.strings
126 .insert(StringKey::Notes, StringValue::new(val, common::PROTECT_NOTES_DEFAULT));
127 }
128
129 pub fn set_other<S: Into<String>>(&mut self, key: StringKey, val: S) {
131 self.strings.insert(key, StringValue::new(val, false));
132 }
133
134 pub fn set_password<S: Into<String>>(&mut self, val: S) {
136 self.strings
137 .insert(StringKey::Password, StringValue::new(val, common::PROTECT_PASSWORD_DEFAULT));
138 }
139
140 pub fn set_title<S: Into<String>>(&mut self, val: S) {
142 self.strings
143 .insert(StringKey::Title, StringValue::new(val, common::PROTECT_TITLE_DEFAULT));
144 }
145
146 pub fn set_url<S: Into<String>>(&mut self, val: S) {
148 self.strings
149 .insert(StringKey::Url, StringValue::new(val, common::PROTECT_URL_DEFAULT));
150 }
151
152 pub fn set_username<S: Into<String>>(&mut self, val: S) {
154 self.strings
155 .insert(StringKey::Username, StringValue::new(val, common::PROTECT_USERNAME_DEFAULT));
156 }
157
158 pub fn title(&self) -> Option<&str> {
160 self.other(StringKey::Title)
161 }
162
163 pub fn url(&self) -> Option<&str> {
165 self.other(StringKey::Url)
166 }
167
168 pub fn username(&self) -> Option<&str> {
170 self.other(StringKey::Username)
171 }
172}
173
174impl Default for Entry {
175 fn default() -> Entry {
176 let now = Utc::now();
177 Entry {
178 associations: Vec::new(),
179 auto_type_def_sequence: String::new(),
180 auto_type_enabled: true,
181 auto_type_obfuscation: Obfuscation::None,
182 background_color: None,
183 binaries: HashMap::new(),
184 creation_time: now,
185 custom_icon_uuid: None,
186 expires: false,
187 expiry_time: now,
188 foreground_color: None,
189 history: Vec::new(),
190 icon: Icon::Key,
191 last_accessed: now,
192 last_modified: now,
193 location_changed: now,
194 override_url: String::new(),
195 strings: StringsMap::new(),
196 tags: String::new(),
197 usage_count: 0,
198 uuid: EntryUuid::nil(),
199 parent: GroupUuid::nil(),
200 }
201 }
202}
203
204impl Times for Entry {
205 fn creation_time(&self) -> DateTime<Utc> {
206 self.creation_time
207 }
208
209 fn expires(&self) -> bool {
210 self.expires
211 }
212
213 fn expiry_time(&self) -> DateTime<Utc> {
214 self.expiry_time
215 }
216
217 fn last_accessed(&self) -> DateTime<Utc> {
218 self.last_accessed
219 }
220
221 fn last_modified(&self) -> DateTime<Utc> {
222 self.last_modified
223 }
224
225 fn location_changed(&self) -> DateTime<Utc> {
226 self.location_changed
227 }
228
229 fn usage_count(&self) -> i32 {
230 self.usage_count
231 }
232
233 fn set_creation_time(&mut self, val: DateTime<Utc>) {
234 self.creation_time = val;
235 }
236
237 fn set_expires(&mut self, val: bool) {
238 self.expires = val;
239 }
240
241 fn set_expiry_time(&mut self, val: DateTime<Utc>) {
242 self.expiry_time = val;
243 }
244
245 fn set_last_accessed(&mut self, val: DateTime<Utc>) {
246 self.last_accessed = val;
247 }
248
249 fn set_last_modified(&mut self, val: DateTime<Utc>) {
250 self.last_modified = val;
251 }
252
253 fn set_location_changed(&mut self, val: DateTime<Utc>) {
254 self.location_changed = val;
255 }
256
257 fn set_usage_count(&mut self, val: i32) {
258 self.usage_count = val;
259 }
260}
261
262#[cfg(test)]
263mod tests {
264
265 use super::*;
266 use crate::types::EntryUuid;
267 use crate::types::Icon;
268 use crate::types::Obfuscation;
269 use crate::types::StringKey;
270 use crate::types::StringsMap;
271 use crate::utils::test::approx_equal_datetime;
272 use chrono::Utc;
273 use std::collections::HashMap;
274
275 #[test]
276 fn test_new_returns_correct_instance() {
277 let now = Utc::now();
278 let entry = Entry::new();
279 assert_eq!(entry.associations, Vec::new());
280 assert_eq!(entry.auto_type_def_sequence, "");
281 assert_eq!(entry.auto_type_enabled, true);
282 assert_eq!(entry.auto_type_obfuscation, Obfuscation::None);
283 assert_eq!(entry.background_color, None);
284 assert_eq!(entry.binaries, HashMap::new());
285 assert!(approx_equal_datetime(entry.creation_time, now));
286 assert_eq!(entry.custom_icon_uuid, None);
287 assert_eq!(entry.expires, false);
288 assert!(approx_equal_datetime(entry.expiry_time, now));
289 assert_eq!(entry.foreground_color, None);
290 assert_eq!(entry.history, Vec::new());
291 assert_eq!(entry.icon, Icon::Key);
292 assert!(approx_equal_datetime(entry.last_accessed, now));
293 assert!(approx_equal_datetime(entry.last_modified, now));
294 assert!(approx_equal_datetime(entry.location_changed, now));
295 assert_eq!(entry.override_url, "");
296 assert_eq!(entry.strings, StringsMap::new());
297 assert_eq!(entry.tags, "");
298 assert_eq!(entry.usage_count, 0);
299 assert!(entry.uuid != EntryUuid::nil());
300 }
301
302 #[test]
303 fn test_notes_returns_none_on_default_entry() {
304 let entry = Entry::default();
305 assert_eq!(entry.notes(), None);
306 }
307
308 #[test]
309 fn test_other_returns_none_on_default_entry() {
310 let entry = Entry::default();
311 let key = StringKey::from_string("other");
312 assert_eq!(entry.other(key), None);
313 }
314
315 #[test]
316 fn test_password_returns_none_on_default_entry() {
317 let entry = Entry::default();
318 assert_eq!(entry.password(), None);
319 }
320
321 #[test]
322 fn test_set_notes_sets_notes() {
323 let mut entry = Entry::default();
324 entry.set_notes("test");
325 assert_eq!(entry.notes(), Some("test"));
326 }
327
328 #[test]
329 fn test_set_other_sets_other() {
330 let mut entry = Entry::default();
331 let key = StringKey::from_string("other");
332 entry.set_other(key.clone(), "test");
333 assert_eq!(entry.other(key), Some("test"));
334 }
335
336 #[test]
337 fn test_set_password_sets_password() {
338 let mut entry = Entry::default();
339 entry.set_password("test");
340 assert_eq!(entry.password(), Some("test"));
341 }
342
343 #[test]
344 fn test_set_title_sets_title() {
345 let mut entry = Entry::default();
346 entry.set_title("test");
347 assert_eq!(entry.title(), Some("test"));
348 }
349
350 #[test]
351 fn test_set_url_sets_url() {
352 let mut entry = Entry::default();
353 entry.set_url("test");
354 assert_eq!(entry.url(), Some("test"));
355 }
356
357 #[test]
358 fn test_set_username_sets_username() {
359 let mut entry = Entry::default();
360 entry.set_username("test");
361 assert_eq!(entry.username(), Some("test"));
362 }
363
364 #[test]
365 fn test_title_returns_none_on_default_entry() {
366 let entry = Entry::default();
367 assert_eq!(entry.title(), None);
368 }
369
370 #[test]
371 fn test_url_returns_none_on_default_entry() {
372 let entry = Entry::default();
373 assert_eq!(entry.url(), None);
374 }
375
376 #[test]
377 fn test_username_returns_none_on_default_entry() {
378 let entry = Entry::default();
379 assert_eq!(entry.username(), None);
380 }
381
382 #[test]
383 fn test_default_returns_correct_instance() {
384 let now = Utc::now();
385 let entry = Entry::default();
386 assert_eq!(entry.associations, Vec::new());
387 assert_eq!(entry.auto_type_def_sequence, "");
388 assert_eq!(entry.auto_type_enabled, true);
389 assert_eq!(entry.auto_type_obfuscation, Obfuscation::None);
390 assert_eq!(entry.background_color, None);
391 assert_eq!(entry.binaries, HashMap::new());
392 assert!(approx_equal_datetime(entry.creation_time, now));
393 assert_eq!(entry.custom_icon_uuid, None);
394 assert_eq!(entry.expires, false);
395 assert!(approx_equal_datetime(entry.expiry_time, now));
396 assert_eq!(entry.foreground_color, None);
397 assert_eq!(entry.history, Vec::new());
398 assert_eq!(entry.icon, Icon::Key);
399 assert!(approx_equal_datetime(entry.last_accessed, now));
400 assert!(approx_equal_datetime(entry.last_modified, now));
401 assert!(approx_equal_datetime(entry.location_changed, now));
402 assert_eq!(entry.override_url, "");
403 assert_eq!(entry.strings, StringsMap::new());
404 assert_eq!(entry.tags, "");
405 assert_eq!(entry.usage_count, 0);
406 assert_eq!(entry.uuid, EntryUuid::nil());
407 }
408}