1use std::{sync::Arc, time::Duration};
2
3use cache::CardCache;
4use card::SavedCard;
5use filter::FilterUtil;
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7use uuid::Uuid;
8
9pub mod cache;
10pub mod card;
11pub mod categories;
12pub mod common;
13pub mod config;
14pub mod filter;
15pub mod git;
16pub mod media;
17pub enum CardsAction {
21 Review,
22 SetSuspended(bool),
23}
24
25#[derive(Default, Clone)]
26enum Field {
27 _Strength,
28 #[default]
29 LastModified,
30}
31
32#[derive(Default, Clone)]
33struct CardSorter {
34 ascending: bool,
35 field: Field,
36}
37
38#[derive(Clone)]
39enum Mode {
40 List { index: usize },
41 View { index: usize },
42}
43
44impl Mode {
45 fn toggle(&mut self) {
46 *self = match self {
47 Self::List { index } => Self::View { index: *index },
48 Self::View { index } => Self::List { index: *index },
49 }
50 }
51
52 fn is_view(&self) -> bool {
53 match self {
54 Mode::List { .. } => false,
55 Mode::View { .. } => true,
56 }
57 }
58
59 fn is_list(&self) -> bool {
60 !self.is_view()
61 }
62
63 fn enter_list(&mut self) {
64 let index = self.idx();
65 *self = Self::List { index };
66 }
67 fn enter_view(&mut self) {
68 let index = self.idx();
69 *self = Self::View { index };
70 }
71
72 fn idx(&self) -> usize {
73 match self {
74 Mode::List { index } => *index,
75 Mode::View { index } => *index,
76 }
77 }
78
79 fn mut_idx(&mut self) -> &mut usize {
80 match self {
81 Mode::List { index } => index,
82 Mode::View { index } => index,
83 }
84 }
85
86 pub fn set_idx(&mut self, idx: usize) {
87 *self.mut_idx() = idx;
88 }
89}
90
91impl Default for Mode {
92 fn default() -> Self {
93 Self::List { index: 0 }
94 }
95}
96
97#[derive(Clone)]
99pub struct CardViewer {
100 cards: Vec<Id>,
101 filtered_cards: Vec<Id>,
102 filter: FilterUtil,
103 sorter: CardSorter,
104 mode: Mode,
105}
106
107impl CardViewer {
108 pub fn new(cards: Vec<Id>) -> Self {
109 if cards.is_empty() {
110 panic!("plz at least one element");
111 }
112
113 Self {
114 cards: cards.clone(),
115 filtered_cards: cards,
116 filter: FilterUtil::default(),
117 sorter: CardSorter::default(),
118 mode: Mode::default(),
119 }
120 }
121
122 pub fn total_qty(&self) -> usize {
123 self.cards.len()
124 }
125
126 pub fn set_idx(&mut self, idx: usize) {
127 self.mode.set_idx(idx);
128 }
129
130 pub fn filtered_qty(&self) -> usize {
131 self.filtered_cards.len()
132 }
133
134 fn update_filtered_cards(&mut self, mut cards: Vec<Id>, cache: &mut CardCache) {
135 match self.sorter.field {
136 Field::_Strength => {
137 cards.sort_by_key(|card| cache.get_ref(*card).strength().unwrap_or_default())
138 }
139 Field::LastModified => cards.sort_by_key(|card| cache.get_ref(*card).last_modified()),
140 }
141 if !self.sorter.ascending {
142 cards.reverse();
143 }
144 self.filtered_cards = cards;
145 }
146
147 pub fn go_forward(&mut self) {
148 let card_len = self.filtered_cards.len();
149 let idx = self.mode.mut_idx();
150
151 if *idx < card_len - 1 {
152 *idx += 1;
153 }
154 }
155
156 pub fn filtered_cards(&self) -> &Vec<Id> {
157 &self.filtered_cards
158 }
159
160 pub fn is_view(&self) -> bool {
161 self.mode.is_view()
162 }
163
164 pub fn is_list(&self) -> bool {
165 self.mode.is_list()
166 }
167
168 pub fn filter_ref(&self) -> &FilterUtil {
169 &self.filter
170 }
171
172 pub fn enter_view(&mut self) {
173 self.mode.enter_view();
174 }
175
176 pub fn enter_list(&mut self) {
177 self.mode.enter_list();
178 }
179
180 pub fn idx(&self) -> usize {
181 self.mode.idx()
182 }
183
184 pub fn selected_card_id(&self) -> Id {
185 self.filtered_cards[self.idx()]
186 }
187
188 pub fn selected_card(&self, cache: &mut CardCache) -> Arc<SavedCard> {
189 let idx = self.mode.idx();
190 cache.get_ref(self.filtered_cards[idx])
191 }
192
193 pub fn go_back(&mut self) {
194 let idx = self.mode.mut_idx();
195
196 if *idx > 0 {
197 *idx -= 1;
198 }
199 }
200
201 pub fn filter_mut(&mut self) -> &mut FilterUtil {
202 &mut self.filter
203 }
204
205 pub fn re_apply_rules(&mut self, cache: &mut CardCache) {
206 let cards = self.filter.evaluate_cards(self.cards.clone(), cache);
207
208 if !cards.is_empty() {
209 self.filtered_cards = cards;
210 }
211 }
212
213 pub fn filter(mut self, filter: FilterUtil, cache: &mut CardCache) -> Self {
214 let filtered_cards = filter.evaluate_cards(self.cards.clone(), cache);
215 if filtered_cards.is_empty() {
216 return self;
217 }
218 self.update_filtered_cards(filtered_cards, cache);
219 self.filter = filter;
220 *self.mode.mut_idx() = 0;
221 self
222 }
223
224 pub fn toggle_mode(&mut self) {
225 self.mode.toggle();
226 }
227}
228
229pub mod paths {
230 use std::path::PathBuf;
231
232 pub fn get_import_csv() -> PathBuf {
233 get_share_path().join("import.csv")
234 }
235
236 pub fn get_cards_path() -> PathBuf {
237 get_share_path().join("cards")
238 }
239
240 pub fn get_ml_path() -> PathBuf {
241 get_share_path().join("ml/")
242 }
243
244 pub fn get_runmodel_path() -> PathBuf {
245 get_ml_path().join("runmodel.py")
246 }
247
248 pub fn get_media_path() -> PathBuf {
249 get_share_path().join("media/")
250 }
251
252 #[cfg(not(test))]
253 pub fn get_share_path() -> PathBuf {
254 let home = dirs::home_dir().unwrap();
255 home.join(".local/share/speki/")
256 }
257
258 #[cfg(test)]
259 pub fn get_share_path() -> PathBuf {
260 PathBuf::from("./test_dir/")
261 }
262}
263
264pub type Id = Uuid;
265
266pub fn duration_to_days(dur: &Duration) -> f32 {
267 dur.as_secs_f32() / 86400.
268}
269
270pub fn days_to_duration(days: f32) -> Duration {
271 Duration::from_secs_f32(days * 86400.)
272}
273
274pub fn serialize_duration<S>(duration: &Option<Duration>, serializer: S) -> Result<S::Ok, S::Error>
275where
276 S: Serializer,
277{
278 let days = duration.as_ref().map(duration_to_days);
279 days.serialize(serializer)
280}
281
282pub fn deserialize_duration<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
283where
284 D: Deserializer<'de>,
285{
286 let days = Option::<f32>::deserialize(deserializer)?;
287 Ok(days.map(days_to_duration))
288}