1use crate::prelude::*;
8use alloc::collections::BTreeSet;
9
10use crate::storage::{Storage, StorageError};
11
12pub const TIPS_SEEN_KEY: &str = "tips_seen";
15
16#[derive(Debug, Clone, Default, PartialEq, Eq)]
18pub struct TipsSeen {
19 ids: BTreeSet<String>,
20}
21
22impl TipsSeen {
23 pub fn load(storage: &dyn Storage) -> Self {
26 let saved = storage.read(TIPS_SEEN_KEY).ok().flatten();
27 let ids = saved
28 .iter()
29 .flat_map(|s| s.lines())
30 .map(str::trim)
31 .filter(|l| !l.is_empty())
32 .map(str::to_owned)
33 .collect();
34 Self { ids }
35 }
36
37 pub fn contains(&self, id: &str) -> bool {
39 self.ids.contains(id)
40 }
41
42 pub fn mark(&mut self, id: &str, storage: &mut dyn Storage) {
46 if self.ids.insert(id.to_owned()) {
47 let text: Vec<&str> = self.ids.iter().map(String::as_str).collect();
48 storage.write(TIPS_SEEN_KEY, &text.join("\n")).ok();
49 }
50 }
51}
52
53pub fn reset_tips(storage: &mut dyn Storage) -> Result<(), StorageError> {
56 storage.delete(TIPS_SEEN_KEY)
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use crate::storage::MemoryStorage;
63
64 #[test]
65 fn seen_tips_are_saved_and_loaded() {
66 let mut storage = MemoryStorage::new();
67 let mut seen = TipsSeen::load(&storage);
68 assert!(!seen.contains("a"));
69 seen.mark("a", &mut storage);
70 seen.mark("b", &mut storage);
71 seen.mark("a", &mut storage);
72 assert_eq!(
73 storage.read(TIPS_SEEN_KEY).unwrap().as_deref(),
74 Some("a\nb")
75 );
76 let again = TipsSeen::load(&storage);
77 assert!(again.contains("a") && again.contains("b") && !again.contains("c"));
78 assert_eq!(again, seen);
79 storage.write(TIPS_SEEN_KEY, " c \n\n \nd").unwrap();
81 let read = TipsSeen::load(&storage);
82 assert!(read.contains("c") && read.contains("d"));
83 assert_eq!(read.ids.len(), 2);
84 }
85
86 #[test]
87 fn resetting_forgets_every_tip() {
88 let mut storage = MemoryStorage::new();
89 TipsSeen::default().mark("a", &mut storage);
90 let mut seen = TipsSeen::load(&storage);
91 seen.mark("b", &mut storage);
92 reset_tips(&mut storage).unwrap();
93 assert_eq!(TipsSeen::load(&storage), TipsSeen::default());
94 reset_tips(&mut storage).unwrap();
96 }
97}