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