speki_backend/
cache.rs

1use std::collections::{BTreeSet, HashMap, VecDeque};
2
3use std::sync::Arc;
4
5use crate::card::SavedCard;
6use crate::common::{get_last_modified, system_time_as_unix_time};
7use crate::Id;
8
9#[derive(Debug, Default)]
10pub struct CardCache(HashMap<Id, Arc<SavedCard>>);
11
12impl CardCache {
13    /// Checks that the card in the cache is up to date, and fixes it if it's not.
14    /// There's three possibilities:
15    ///     1. its up to date, no need to do anything.
16    ///     2. It's outdated, we simply deserialize the card from the same path, so that its updated.
17    ///     3. card isn't even found in the location, we search through all the cards to find it. Panicking if it's not found.
18    fn maybe_update(&mut self, id: Id) {
19        let card_needs_update = match self.0.get(&id) {
20            Some(cached_card) => {
21                let path = cached_card.as_path();
22                if path.exists() {
23                    // Get the file's last_modified time
24                    let metadata = std::fs::metadata(path.as_path()).unwrap();
25                    let last_modified_time = system_time_as_unix_time(metadata.modified().unwrap());
26
27                    // Check if the file has been modified since we cached it
28                    Some(last_modified_time > cached_card.last_modified())
29                } else {
30                    None
31                }
32            }
33            None => None, // If card isn't in the cache, then we definitely need to update
34        };
35
36        match card_needs_update {
37            Some(true) => {
38                let path = self.0.get(&id).unwrap().as_path();
39                let updated_card = SavedCard::from_path(path.as_path());
40                self.0.insert(id, updated_card.into());
41            }
42            // if you find the card, and it's up to date, then no need to do anything.
43            Some(false) => {}
44            None => {
45                // Read the card from the disk
46                // expensive! it'll comb through all the cards linearly.
47                if let Some(card) = SavedCard::from_id(&id) {
48                    self.0.insert(id, card.into());
49                };
50            }
51        };
52    }
53
54    pub fn ids_as_vec(&self) -> Vec<Id> {
55        self.0.keys().copied().collect()
56    }
57
58    /// gets all the Ids (keys) sorted by recent modified
59    pub fn all_ids(&self) -> Vec<Id> {
60        let mut pairs: Vec<_> = self.0.iter().collect();
61        pairs.sort_by_key(|&(_, v)| {
62            if v.is_outdated() {
63                get_last_modified(v.as_path())
64            } else {
65                v.last_modified().to_owned()
66            }
67        });
68        pairs.reverse();
69        pairs.into_iter().map(|(k, _)| k.to_owned()).collect()
70    }
71
72    pub fn exists(&self, id: &Id) -> bool {
73        self.0.get(id).is_some()
74    }
75
76    pub fn insert(&mut self, card: SavedCard) {
77        let id = card.id();
78        self.0.insert(id, card.into());
79    }
80
81    pub fn remove(&mut self, id: Id) {
82        self.0.remove(&id);
83    }
84
85    pub fn dependencies(&mut self, id: Id) -> BTreeSet<Id> {
86        let Some(card) = self.try_get_ref(id) else {
87            return Default::default();
88        };
89
90        card.dependency_ids()
91            .iter()
92            .map(ToOwned::to_owned)
93            .collect()
94    }
95
96    pub fn dependents(&mut self, id: Id) -> BTreeSet<Id> {
97        let Some(card) = self.try_get_ref(id) else {
98            return Default::default();
99        };
100
101        card.dependent_ids().iter().map(ToOwned::to_owned).collect()
102    }
103
104    pub fn recursive_dependencies(&mut self, id: Id) -> BTreeSet<Id> {
105        let mut dependencies = BTreeSet::new();
106        let mut stack = VecDeque::new();
107        stack.push_back(id);
108
109        while let Some(card) = stack.pop_back() {
110            if !dependencies.contains(&card) {
111                dependencies.insert(card);
112
113                let card_dependencies = self.dependencies(card);
114
115                for dependency in card_dependencies {
116                    stack.push_back(dependency);
117                }
118            }
119        }
120
121        dependencies.remove(&id);
122        dependencies
123    }
124
125    pub fn recursive_dependents(&mut self, id: Id) -> BTreeSet<Id> {
126        let mut dependencies = BTreeSet::new();
127        let mut stack = VecDeque::new();
128        stack.push_back(id);
129
130        while let Some(card) = stack.pop_back() {
131            if !dependencies.contains(&card) {
132                dependencies.insert(card);
133
134                let card_dependencies = self.dependents(card);
135
136                for dependency in card_dependencies {
137                    stack.push_back(dependency);
138                }
139            }
140        }
141
142        dependencies.remove(&id);
143        dependencies
144    }
145
146    pub fn try_get_ref(&mut self, id: Id) -> Option<Arc<SavedCard>> {
147        self.maybe_update(id);
148        self.0.get(&id).cloned()
149    }
150
151    pub fn get_owned(&mut self, id: Id) -> SavedCard {
152        (*self.get_ref(id)).clone()
153    }
154
155    pub fn get_ref(&mut self, id: Id) -> Arc<SavedCard> {
156        self.try_get_ref(id).unwrap()
157    }
158
159    pub fn new() -> Self {
160        let mut cache = Self::default();
161        cache.cache_all();
162        cache
163    }
164
165    pub fn refresh(&mut self) {
166        *self = Self::new();
167    }
168
169    fn cache_all(&mut self) {
170        let all_cards = SavedCard::load_all_cards();
171        for card in all_cards {
172            self.cache_one(card);
173        }
174    }
175
176    pub fn cache_one(&mut self, card: SavedCard) {
177        self.0.insert(card.id(), card.into());
178    }
179
180    pub fn new_empty() -> Self {
181        CardCache(HashMap::new())
182    }
183}