rustix_bl/
datastore.rs

1// An attribute to hide warnings for unused code.
2#![allow(dead_code)]
3
4
5use std::collections::HashSet;
6use std::collections::HashMap;
7use left_threaded_avl_tree::ScoredIdTreeMock;
8use suffix_rs::*;
9use suffix_rs::KDTree;
10use left_threaded_avl_tree::AVLTree;
11use typescriptify::TypeScriptifyTrait;
12
13pub trait DatastoreQueries {
14    fn get_purchase_timestamp(&self, purchase_id: u64) -> Option<i64>;
15    fn top_user_ids(&self, n : u16) -> Vec<u32>;
16    fn top_item_ids(&self, user_id: u32, n : u8) -> Vec<u32>;
17
18    fn users_searchhit_ids(&self, searchterm: &str) -> Vec<u32>;
19    fn items_searchhit_ids(&self, searchterm: &str) -> Vec<u32>;
20
21    fn personal_log_filtered(&self, user_id: u32, millis_start_inclusive: i64, millis_end_exclusive: i64) -> Vec<Purchase>;
22    fn global_log_filtered(&self, millis_start_inclusive: i64, millis_end_exclusive: i64) -> &[Purchase];
23
24    fn bills_filtered(&self, user_id: Option<u32>, millis_start_inclusive: i64, millis_end_exclusive: i64) -> Vec<Bill>;
25
26    fn all_categories(&self) -> Vec<String>;
27
28
29    fn get_mut_purchase(&mut self, id: &u64) -> Option<&mut Purchase>;
30    fn get_bill(&self, timestamp_from: i64, timestamp_to: i64) -> Option<&Bill>;
31    fn get_mut_bill(&mut self, timestamp_from: i64, timestamp_to: i64) -> Option<&mut Bill>;
32
33
34    fn get_specials_to_bill(&self, timestamp_from: i64, timestamp_to: i64) -> Vec<u64>;
35
36    fn get_unpriced_specials_to_bill(&self, timestamp_from: i64, timestamp_to: i64) -> Vec<u64>;
37
38    //can have external_id, not_billed flag set, or in ignore list. Will still be shown
39    fn get_users_to_bill(&self, timestamp_from: i64, timestamp_to: i64) -> Vec<u32>;
40    fn get_un_set_users_to_bill(&self, timestamp_from: i64, timestamp_to: i64) -> Vec<u32>;
41    fn get_bill_index(&mut self, timestamp_from: i64, timestamp_to: i64) -> Option<usize>;
42    fn get_purchase_indices_to_bill(&self, bill: &Bill) -> Vec<usize>;
43
44
45    fn remove_purchases_indices(&mut self, indices: Vec<usize>);
46
47
48    fn get_ffa_freeby(&self, id: u64) -> Option<&Freeby>;
49    fn get_personal_freeby(&self, recipient_id: u32, freeby_id: u64) -> Option<&Freeby>;
50
51
52
53
54    fn get_budget_freeby_id_useable_for(&self, recipient_id: u32) -> Option<usize>;
55    fn get_count_freeby_id_useable_for(&self, recipient_id: u32, item : u32) -> Option<usize>;
56}
57
58
59pub trait SuffixTreeRebuildable {
60    fn rebuild_user_tree(&self) -> ();
61    fn rebuild_item_tree(&self) -> ();
62}
63
64
65#[derive(Debug, Serialize, Deserialize)]
66pub struct Datastore {
67
68    pub version: u64,
69    pub user_id_counter: u32,
70    pub freeby_id_counter: u64,
71    pub item_id_counter: u32,
72
73    pub users: HashMap<u32, User>,
74    pub users_suffix_tree: MockKDTree,
75    pub items: HashMap<u32, Item>,
76    pub items_suffix_tree: MockKDTree,
77    pub purchases: Vec<Purchase>,
78    pub purchase_count: u64,
79    pub bills: Vec<Bill>,
80    pub top_user_scores: ScoredIdTreeMock,
81    pub top_users: HashSet<u32>,
82    pub highlighted_users: HashSet<u32>,
83    pub top_drinks_per_user: HashMap<u32, HashSet<u32>>,
84    pub drink_scores_per_user: HashMap<u32, ScoredIdTreeMock>,
85    pub balance_cost_per_user: HashMap<(u32, String), HashMap<(u32, String), u32>>,
86    pub balance_count_per_user: HashMap<(u32, String), HashMap<(u32, String), u32>>,
87    pub used_up_freebies: Vec<Freeby>, //completely mixed
88    pub open_freebies: HashMap<u32, Vec<Freeby>>, //per recipient
89    pub open_ffa: Vec<Freeby>,
90
91    // keeps hashmap of user_id => user
92    // keeps hashmap of user_id => user
93    // keeps bill-vector
94    // keeps scoring tree for saufbubbies / all users
95    // keeps saufbubbies
96    // keeps paginated user pages
97    // keeps categorized item pages
98    // keeps per user item scoring tree
99    // keeps per user item simplified bill (hashmap<name,hasmap<price,number>>)
100    pub categories: HashSet<String>,
101
102
103}
104
105
106
107impl SuffixTreeRebuildable for Datastore {
108    fn rebuild_user_tree(&self) -> () {
109        unimplemented!()
110    }
111
112    fn rebuild_item_tree(&self) -> () {
113        unimplemented!()
114    }
115}
116
117
118impl DatastoreQueries for Datastore {
119
120    fn get_purchase_timestamp(&self, purchase_id: u64) -> Option<i64> {
121        return self.get_purchase(purchase_id).map(|p| *(p.get_timestamp()));
122    }
123
124    fn top_user_ids(&self, n: u16) -> Vec<u32> {
125        return self.top_user_scores.extract_top(n as usize);
126    }
127
128    fn users_searchhit_ids(&self, searchterm: &str) -> Vec<u32> {
129        let mut v : Vec<u32> = vec![];
130        let xs =  self.users_suffix_tree.search(searchterm);
131        for x in xs {
132            v.push(x.id);
133        }
134        return v;
135    }
136
137    fn items_searchhit_ids(&self, searchterm: &str) -> Vec<u32> {
138        return self.items_suffix_tree.search(searchterm).iter().map(|sr : &SearchResult|sr.id).collect();
139    }
140
141    fn personal_log_filtered(&self, user_id: u32, millis_start_inclusive: i64, millis_end_exclusive: i64) -> Vec<Purchase> {
142        let v : Vec<Purchase> = self.purchases.iter()
143            .filter(|p: &&Purchase| {
144            p.get_user_id().eq(&user_id) && p.get_timestamp() >= &millis_start_inclusive && p.get_timestamp() < &millis_end_exclusive
145        })
146            .map(|p: &Purchase| p.clone())
147            .collect();
148
149        return v;
150    }
151
152    fn global_log_filtered(&self, millis_start_inclusive: i64, millis_end_exclusive: i64) -> &[Purchase] {
153        assert!(millis_start_inclusive <= millis_end_exclusive);
154
155        let (from, to) = find_purchase_indices(&self.purchases, millis_start_inclusive, millis_end_exclusive);
156
157        return &self.purchases[from..to];
158    }
159
160    fn all_categories(&self) -> Vec<String> {
161        let mut v : Vec<String> = Vec::new();
162        for x in &self.categories {
163            v.push(x.to_string());
164        }
165        return v;
166    }
167    fn top_item_ids(&self, user_id: u32, n: u8) -> Vec<u32> {
168        match self.drink_scores_per_user.get(&user_id) {
169            Some(ref tree) => return tree.extract_top(n as usize),
170            None => return vec![],
171        };
172    }
173    fn bills_filtered(&self, user_id: Option<u32>, millis_start_inclusive: i64, millis_end_exclusive: i64) -> Vec<Bill> {
174        let v : Vec<Bill> = self.bills.iter()
175            .filter(|b: &&Bill| {
176                matches_usergroup(&user_id, &b.users) && !(b.timestamp_to < millis_start_inclusive || b.timestamp_from > millis_end_exclusive)
177            })
178            .map(|p: &Bill| p.clone())
179            .collect();
180        return v;
181    }
182
183    fn get_mut_purchase(&mut self, id: &u64) -> Option<&mut Purchase> {
184        let idx = self.purchases.binary_search_by(|p| p.get_unique_id().cmp(id));
185
186        return idx.map(move |id| self.purchases.get_mut(id).unwrap()).ok();
187    }
188
189    fn get_mut_bill(&mut self, timestamp_from: i64, timestamp_to: i64) -> Option<&mut Bill> {
190        return self.bills.iter_mut().find(|b| b.timestamp_from == timestamp_from && b.timestamp_to == timestamp_to);
191    }
192
193    fn get_bill_index(&mut self, timestamp_from: i64, timestamp_to: i64) -> Option<usize> {
194        return self.bills.iter().position(|b| b.timestamp_from == timestamp_from && b.timestamp_to == timestamp_to);
195    }
196
197    fn get_specials_to_bill(&self, timestamp_from: i64, timestamp_to: i64) -> Vec<u64> {
198        let (from, to) = find_purchase_indices(&self.purchases, timestamp_from, timestamp_to);
199        let bill_opt = self.bills.iter().find(|b|b.timestamp_to == timestamp_to && b.timestamp_from == timestamp_from);
200        if bill_opt.is_none() {
201            return Vec::new();
202        } else {
203            let bill = bill_opt.unwrap();
204            return self.purchases[from..to].iter().filter(|p| {
205                p.has_user_id() && matches_usergroup(&Some(*p.get_user_id()), &bill.users) && (match **p {
206                    Purchase::SpecialPurchase {
207                        ..
208                    } => true,
209                    _ => false,
210                })
211            }).map(|p|p.get_unique_id()).collect();
212        }
213    }
214
215    fn get_users_to_bill(&self, timestamp_from: i64, timestamp_to: i64) -> Vec<u32> {
216        let bill_opt = self.bills.iter().find(|b|b.timestamp_to == timestamp_to && b.timestamp_from == timestamp_from);
217        if bill_opt.is_none() {
218            return Vec::new();
219        } else {
220            let bill = bill_opt.unwrap();
221
222            let mut xs : Vec<u32> = vec![];
223
224            let filtered = self.users.iter().filter(|kv| !kv.1.deleted && matches_usergroup(&Some(*kv.0), &bill.users));
225
226            for keyvalue in filtered {
227                xs.push(*keyvalue.0);
228            }
229
230                return xs;
231        }
232    }
233
234    fn get_un_set_users_to_bill(&self, timestamp_from: i64, timestamp_to: i64) -> Vec<u32> {
235        let bill_opt = self.bills.iter().find(|b|b.timestamp_to == timestamp_to && b.timestamp_from == timestamp_from);
236        if bill_opt.is_none() {
237            return Vec::new();
238        } else {
239            let bill = bill_opt.unwrap();
240
241
242            let mut touched_users_set: HashSet<u32> = HashSet::new();
243            let mut users_undefined_indices: Vec<u32> = vec![];
244
245
246            for purchase in self.global_log_filtered(timestamp_from, timestamp_to) {
247                let uid: u32 = *purchase.get_user_id();
248                if matches_usergroup(&Some(uid), &bill.users) {
249                    if !touched_users_set.contains(&uid) {
250                        //user matches criteria & isn't in list => add user to list
251                        touched_users_set.insert(uid);
252                        let usr = self.users.get(&uid).unwrap();
253
254                        if !usr.is_billed {
255                            //if user isn't billed per field, add to externally excluded list
256                        } else if bill.users_that_will_not_be_billed.contains(&uid) {
257                            //else if user is in internal exclusion list of bill, add to internally excluded list
258                        } else if usr.external_user_id.is_none() {
259                            // else add user to other list
260                            users_undefined_indices.push(uid);
261                        } else if usr.external_user_id.is_some() {
262                        }
263                    }
264                }
265            }
266
267            return users_undefined_indices;
268        }
269    }
270    fn get_unpriced_specials_to_bill(&self, timestamp_from: i64, timestamp_to: i64) -> Vec<u64>
271        {
272
273            let mut xs : Vec<u64> = Vec::new();
274            {
275                use datastore::PurchaseFunctions;
276
277                self.get_specials_to_bill(timestamp_from, timestamp_to).iter().map(|id| self.get_purchase(*id).unwrap()).filter(|p|p.get_special_set_price().is_none()).for_each(|p| xs.push(p.get_unique_id()));
278            }
279            return xs;
280        }
281    fn get_bill(&self, timestamp_from: i64, timestamp_to: i64) -> Option<&Bill> {
282        return self.bills.iter().find(|b| b.timestamp_from == timestamp_from && b.timestamp_to == timestamp_to);
283    }
284
285
286    fn get_purchase_indices_to_bill(&self, bill: &Bill) -> Vec<usize> {
287        //from all purchases, get indices inside bill date
288        //filter by donor / consumer being inside bill
289        let (from, to) = find_purchase_indices(&self.purchases, bill.timestamp_from, bill.timestamp_to);
290
291        let mut v: Vec<usize> = vec![];
292        for i in from..to {
293            if matches_usergroup(&Some(*self.purchases[i].get_user_id()), &bill.users)  {
294                v.push(i);
295            }
296        }
297        return v;
298    }
299
300    fn remove_purchases_indices(&mut self, mut indices: Vec<usize>) {
301        indices.sort();
302        for idx in indices.iter().rev() {
303            self.purchases.remove(*idx);
304        }
305    }
306    fn get_budget_freeby_id_useable_for(&self, recipient_id: u32) -> Option<usize> {
307        for (idx, freeby) in self.open_freebies.get(&recipient_id).unwrap_or(&Vec::new()).iter().enumerate() {
308            match *freeby {
309                Freeby::Transfer { .. } => {
310                  return Some(idx);
311                },
312                _ => (),
313            }
314        }
315        return None;
316    }
317
318    fn get_count_freeby_id_useable_for(&self, recipient_id: u32, item : u32) -> Option<usize> {
319        let cat : Option<String> = self.items.get(&item).unwrap().clone().category;
320        for (idx, freeby) in self.open_freebies.get(&recipient_id).unwrap_or(&Vec::new()).iter().enumerate() {
321            match *freeby {
322                Freeby::Classic { ref allowed_categories,
323                    ref allowed_drinks, .. } => {
324                    let cat = cat.clone();
325                    if allowed_drinks.contains(&item) {
326                        return Some(idx);
327                    } else {
328                        match cat {
329                            Some(c) => {
330                                if allowed_categories.contains(&c) {
331                                    return Some(idx);
332                                } else {
333                                    ()
334                                }
335                            },
336                            None => (),
337                        }
338                    }
339                },
340                _ => (),
341            }
342        }
343        return None;
344    }
345    fn get_ffa_freeby(&self, id: u64) -> Option<&Freeby> {
346        let found_open = self.open_ffa.binary_search_by(|f|f.get_id().cmp(&id));
347        if found_open.is_ok() {
348            let found = found_open.unwrap();
349            return self.open_ffa.get(found);
350        } else {
351            let found_closed = self.used_up_freebies.binary_search_by(|f|f.get_id().cmp(&id));
352            if found_closed.is_ok() {
353                let found = found_closed.unwrap();
354                return self.used_up_freebies.get(found);
355            } else {
356                return None;
357            }
358        }
359    }
360
361    fn get_personal_freeby(&self, recipient_id: u32, freeby_id: u64) -> Option<&Freeby> {
362        let found_closed = self.used_up_freebies.binary_search_by(|f|f.get_id().cmp(&freeby_id));
363        if found_closed.is_ok() {
364            return self.used_up_freebies.get(found_closed.unwrap());
365        } else {
366            if self.open_freebies.contains_key(&recipient_id) {
367                let found_open = self.open_freebies.get(&recipient_id).unwrap().binary_search_by(|f| f.get_id().cmp(&freeby_id));
368                if found_open.is_ok() {
369                    return self.open_freebies.get(&recipient_id).unwrap().get(found_open.unwrap());
370                } else {
371                    return None;
372                }
373            } else {
374                return None;
375            }
376        }
377    }
378}
379
380
381pub fn matches_usergroup(user_id: &Option<u32>, usergroup: &UserGroup) -> bool {
382    if user_id.is_some() {
383        let checked_user_id = user_id.clone().unwrap();
384        return match *usergroup {
385            UserGroup::SingleUser {
386                ref user_id
387            } => *user_id == checked_user_id,
388            UserGroup::AllUsers => true,
389            UserGroup::MultipleUsers {
390                ref user_ids
391            } => user_ids.iter().any(|id| *id == checked_user_id),
392        }
393    } else {
394        return true;
395    }
396}
397
398
399fn matches_userset(user_id: &Option<u32>, usergroup: &HashSet<u32>) -> bool {
400    return user_id.map(|id| usergroup.contains(&id)).unwrap_or(true);
401}
402
403//returns lowest and highest vector index to get all purchases in given timespan
404pub fn find_purchase_indices(purchases : &[Purchase], millis_start_inclusive: i64, millis_end_exclusive: i64) -> (usize, usize) {
405    let a = purchases.binary_search_by(|probe|probe.get_timestamp().cmp(&millis_start_inclusive));
406
407    let first = match a {
408        Ok(b) => b,
409        Err(b) => b,
410    };
411
412    let o = purchases.binary_search_by(|probe|{
413        let c = millis_end_exclusive + 1;
414        probe.get_timestamp().cmp(&c)
415    });
416
417    let last = match o {
418        Ok(g) => g,
419        Err(g) => g,
420    };
421
422    return (first,last);
423}
424
425
426pub trait Itemable {
427    fn has_item(&self, id: u32) -> bool;
428}
429
430pub trait Userable {
431    fn has_user(&self, id: u32) -> bool;
432}
433
434pub trait Purchaseable {
435    fn get_purchase(&self, id: u64) -> Option<Purchase>;
436    fn get_purchase_mut(&mut self, id: u64) -> Option<&mut Purchase>;
437}
438
439impl Userable for Datastore {
440    fn has_user(&self, id: u32) -> bool {
441        return self.users.contains_key(&id);
442    }
443}
444impl Itemable for Datastore {
445    fn has_item(&self, id: u32) -> bool {
446        return self.items.contains_key(&id);
447    }
448}
449
450impl Purchaseable for Datastore {
451    fn get_purchase(&self, id: u64) -> Option<Purchase> {
452        match self.purchases.binary_search_by(|p|p.get_unique_id().cmp(&id)) {
453            Ok(idx) => self.purchases.get(idx).map(|p|p.clone()),
454            _ => None,
455        }
456    }
457    fn get_purchase_mut(&mut self, id: u64) -> Option<&mut Purchase> {
458        return self.purchases.iter_mut().find(|p|{
459            p.has_unique_id(id)
460        });
461    }
462}
463
464
465impl SearchableElement for User {
466    fn as_searchable_text(&self) -> String {
467        let s: String = self.username.to_string();
468        return s;
469    }
470
471    fn get_id(&self) -> u32 {
472        return self.user_id;
473    }
474}
475
476
477impl SearchableElement for Item {
478    fn as_searchable_text(&self) -> String {
479        let s: String = self.name.to_string();
480        return s;
481    }
482
483    fn get_id(&self) -> u32 {
484        return self.item_id;
485    }
486}
487
488impl Default for Datastore {
489    fn default() -> Self {
490        let empty_user_vec: Vec<User> = Vec::new();
491        let empty_item_vec: Vec<User> = Vec::new();
492
493        return Datastore {
494            users: HashMap::new(),
495            users_suffix_tree: MockKDTree::build(&empty_user_vec, true),
496            items: HashMap::new(),
497            items_suffix_tree: MockKDTree::build(&empty_item_vec, true),
498            purchases: Vec::new(),
499            purchase_count: 0,
500            bills: Vec::new(),
501            top_user_scores: ScoredIdTreeMock::default(),
502            top_users: HashSet::new(),
503            highlighted_users: HashSet::new(),
504            top_drinks_per_user: HashMap::new(),
505            drink_scores_per_user: HashMap::new(),
506            balance_cost_per_user: HashMap::new(),
507            balance_count_per_user: HashMap::new(),
508            used_up_freebies: Vec::new(),
509            open_freebies: HashMap::new(),
510            open_ffa: Vec::new(),
511            user_id_counter: 0,
512            freeby_id_counter: 0,
513            item_id_counter: 0,
514            categories: HashSet::new(),
515            version: 0,
516        };
517    }
518}
519
520
521
522#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, TypeScriptify)]
523pub enum UserGroup {
524    SingleUser { user_id: u32 },
525    AllUsers,
526    MultipleUsers { user_ids: Vec<u32> },
527}
528
529impl Default for UserGroup {
530    fn default() -> Self {
531        return UserGroup::AllUsers;
532    }
533}
534use rustix_event_shop::default_true;
535
536#[derive(Default, Debug, Serialize, Deserialize, PartialEq, TypeScriptify)]
537pub struct User {
538    pub username: String,
539    pub external_user_id: Option<String>,
540    pub user_id: u32,
541    pub is_billed: bool,
542    #[serde(default="default_true")]
543    pub is_sepa: bool,
544    pub highlight_in_ui: bool,
545    pub deleted: bool,
546}
547
548impl Clone for User {
549    fn clone(&self) -> Self {
550        return User {
551            username: self.username.to_string(),
552            external_user_id: self.external_user_id.clone(),
553            user_id: self.user_id,
554            is_billed: self.is_billed,
555            is_sepa: self.is_sepa,
556            highlight_in_ui: self.highlight_in_ui,
557            deleted: self.deleted,
558        };
559    }
560}
561
562
563
564#[derive(Default, Debug, Serialize, Deserialize, Clone, TypeScriptify)]
565pub struct Item {
566    pub name: String,
567    pub item_id: u32,
568    pub category: Option<String>,
569    pub cost_cents: u32,
570    pub deleted: bool,
571}
572
573
574
575#[derive(Debug, Serialize, Deserialize, Clone, TypeScriptify)]
576pub enum BillState {
577    Created,
578    Finalized,
579    ExportedAtLeastOnce,
580}
581
582impl BillState {
583    pub fn is_created(&self) -> bool {
584        match *self {
585            BillState::Created => return true,
586            _ => return false,
587        }
588    }
589    pub fn is_finalized(&self) -> bool {
590        return !self.is_created()
591    }
592}
593
594impl Default for BillState {
595    fn default() -> Self {
596        return BillState::Created;
597    }
598}
599
600
601
602//for every user in bill the username and id at finalization, plus info if invoice or not, and hashmaps for count of all item ids, also list of specials with pricing for each, and also hashmaps for budgets, hashmap for ffa, and hashmap for count giveouts paid for by others
603//usage is grouped by day
604#[derive(Default, Debug, Serialize, Deserialize, Clone, TypeScriptify)]
605pub struct BillUserInstance {
606    pub user_id: u32,
607    pub per_day: HashMap<usize, BillUserDayInstance>,
608}
609
610#[derive(Default, Debug, Serialize, Deserialize, Clone, TypeScriptify)]
611pub struct BillUserDayInstance {
612    //pub begin_inclusive: i64,
613    //pub end_exclusive: i64,
614
615    pub personally_consumed: HashMap<u32, u32>,
616    pub specials_consumed: Vec<PricedSpecial>,
617
618    pub ffa_giveouts : HashMap<u32, u32>,
619    pub giveouts_to_user_id: HashMap<u32, PaidFor>,
620}
621
622#[derive(Default, Debug, Serialize, Deserialize, Clone, TypeScriptify)]
623pub struct ExportableBillData {
624    pub all_users : HashMap<u32, User>,
625    pub all_items : HashMap<u32, Item>,
626    pub user_consumption: HashMap<u32, BillUserInstance>,
627}
628
629#[derive(Default, Debug, Serialize, Deserialize, Clone, TypeScriptify)]
630pub struct PricedSpecial {
631    pub purchase_id: u64,
632    pub price: u32,
633    pub name: String,
634}
635
636//one instance per user and per person that was given out to (per budget or count giveout)
637#[derive(Default, Debug, Serialize, Deserialize, Clone, TypeScriptify)]
638pub struct PaidFor {
639    pub recipient_id: u32,
640    pub count_giveouts_used: HashMap<u32,u32>,
641    pub budget_given: u64,
642    pub budget_gotten: u64,
643}
644
645
646#[derive(Default, Debug, Serialize, Deserialize, Clone, TypeScriptify)]
647pub struct Bill {
648    //set at creation
649    pub timestamp_from: i64,
650    pub timestamp_to: i64,
651    pub comment: String,
652    pub users: UserGroup,
653    pub bill_state: BillState,
654
655    //set between creation and finalization
656    pub users_that_will_not_be_billed: HashSet<u32>,
657
658    //set at finalization
659    pub finalized_data : ExportableBillData,
660
661}
662
663impl Bill {
664    pub fn get_day_index(&self, time: i64) -> usize {
665        //TODO: deal with real date mechanics here to get correct day at 00:00.000 for every day
666        let day_length: i64 = 1000i64 * 3600i64 * 24i64;
667        let div: i64 = self.timestamp_from / day_length;
668        let first_day_begin: i64 = div * day_length;
669        let day = (time - first_day_begin) / day_length;
670        return day as usize;
671    }
672}
673
674
675pub trait PurchaseFunctions {
676    fn get_unique_id(&self) -> u64;
677    fn has_unique_id(&self, other: u64) -> bool;
678    fn get_user_id(&self) -> &u32;
679    fn has_user_id(&self) -> bool;
680    fn get_item_id(&self) -> &u32;
681    fn has_item_id(&self) -> bool;
682    fn get_timestamp(&self) -> &i64;
683    fn get_special_set_price(&self) -> Option<u32>;
684    fn is_special(&self) -> bool;
685}
686
687
688
689
690
691#[derive(Debug, Serialize, Deserialize, Clone, TypeScriptify)]
692pub enum Freeby {
693    FFA {
694        id: u64,
695        allowed_categories : Vec<String>,
696        allowed_drinks : Vec<u32>,
697        allowed_number_total : u16,
698        allowed_number_used : u16,
699        text_message : String,
700        created_timestamp : i64,
701        donor : u32,
702    },
703    Transfer {
704        id: u64,
705        cents_worth_total : u64,
706        cents_worth_used : u64,
707        text_message : String,
708        created_timestamp : i64,
709        donor : u32,
710        recipient : u32,
711    },
712    Classic {
713        id: u64,
714        allowed_categories : Vec<String>,
715        allowed_drinks : Vec<u32>,
716        allowed_number_total : u16,
717        allowed_number_used : u16,
718        text_message : String,
719        created_timestamp : i64,
720        donor : u32,
721        recipient : u32,
722    },
723}
724
725/*
726return match *self {
727            Freeby::Classic {
728                ref allowed_categories,
729                ref allowed_drinks,
730                ref allowed_number_total,
731                ref allowed_number_used,
732                ref text_message,
733                ref created_timestamp,
734                ref donor,
735                ref recipient
736            } => {
737                uninimplemented!()
738            },
739            Freeby::Transfer{
740                ref cents_worth_total,
741                ref cents_worth_used,
742                ref text_message,
743                ref created_timestamp,
744                ref donor,
745                ref recipient
746            } => {
747                uninimplemented!()
748            },
749            Freeby::FFA {
750                ref allowed_categories,
751                ref allowed_drinks,
752                ref allowed_number_total,
753                ref allowed_number_used,
754                ref text_message,
755                ref created_timestamp,
756                ref donor
757            } => {
758                uninimplemented!()
759            },
760        };
761*/
762
763
764impl FreebyAble for Freeby {
765    fn message(&self) -> &str {
766        return match *self {
767            Freeby::Classic {
768                ref id,
769                ref allowed_categories,
770                ref allowed_drinks,
771                ref allowed_number_total,
772                ref allowed_number_used,
773                ref text_message,
774                ref created_timestamp,
775                ref donor,
776                ref recipient
777            } => {
778                text_message
779            },
780            Freeby::Transfer{
781                ref id,
782                ref cents_worth_total,
783                ref cents_worth_used,
784                ref text_message,
785                ref created_timestamp,
786                ref donor,
787                ref recipient
788            } => {
789                text_message
790            },
791            Freeby::FFA {
792                ref id,
793                ref allowed_categories,
794                ref allowed_drinks,
795                ref allowed_number_total,
796                ref allowed_number_used,
797                ref text_message,
798                ref created_timestamp,
799                ref donor
800            } => {
801                text_message
802            },
803        };
804    }
805    fn get_id(&self) -> u64 {
806        return match *self {
807            Freeby::Classic {
808                ref id,
809                ref allowed_categories,
810                ref allowed_drinks,
811                ref allowed_number_total,
812                ref allowed_number_used,
813                ref text_message,
814                ref created_timestamp,
815                ref donor,
816                ref recipient
817            } => {
818                *id
819            },
820            Freeby::Transfer{
821                ref id,
822                ref cents_worth_total,
823                ref cents_worth_used,
824                ref text_message,
825                ref created_timestamp,
826                ref donor,
827                ref recipient
828            } => {
829                *id
830            },
831            Freeby::FFA {
832                ref id,
833                ref allowed_categories,
834                ref allowed_drinks,
835                ref allowed_number_total,
836                ref allowed_number_used,
837                ref text_message,
838                ref created_timestamp,
839                ref donor
840            } => {
841                *id
842            },
843        };
844    }
845
846    fn get_donor(&self) -> u32 {
847        return match *self {
848            Freeby::Classic {
849                ref id,
850                ref allowed_categories,
851                ref allowed_drinks,
852                ref allowed_number_total,
853                ref allowed_number_used,
854                ref text_message,
855                ref created_timestamp,
856                ref donor,
857                ref recipient
858            } => {
859                *donor
860            },
861            Freeby::Transfer{
862                ref id,
863                ref cents_worth_total,
864                ref cents_worth_used,
865                ref text_message,
866                ref created_timestamp,
867                ref donor,
868                ref recipient
869            } => {
870                *donor
871            },
872            Freeby::FFA {
873                ref id,
874                ref allowed_categories,
875                ref allowed_drinks,
876                ref allowed_number_total,
877                ref allowed_number_used,
878                ref text_message,
879                ref created_timestamp,
880                ref donor
881            } => {
882                *donor
883            },
884        };
885    }
886
887    fn allowed_categories(&self) -> &[String] {
888        return match *self {
889            Freeby::Classic {
890                ref id,
891                ref allowed_categories,
892                ref allowed_drinks,
893                ref allowed_number_total,
894                ref allowed_number_used,
895                ref text_message,
896                ref created_timestamp,
897                ref donor,
898                ref recipient
899            } => {
900                allowed_categories
901            },
902            Freeby::Transfer{
903                ref id,
904                ref cents_worth_total,
905                ref cents_worth_used,
906                ref text_message,
907                ref created_timestamp,
908                ref donor,
909                ref recipient
910            } => {
911                &[]
912            },
913            Freeby::FFA {
914                ref id,
915                ref allowed_categories,
916                ref allowed_drinks,
917                ref allowed_number_total,
918                ref allowed_number_used,
919                ref text_message,
920                ref created_timestamp,
921                ref donor
922            } => {
923                allowed_categories
924            },
925        };
926    }
927
928    fn allowed_items(&self) -> &[u32] {
929        return match *self {
930            Freeby::Classic {
931                ref id,
932                ref allowed_categories,
933                ref allowed_drinks,
934                ref allowed_number_total,
935                ref allowed_number_used,
936                ref text_message,
937                ref created_timestamp,
938                ref donor,
939                ref recipient
940            } => {
941                allowed_drinks
942            },
943            Freeby::Transfer{
944                ref id,
945                ref cents_worth_total,
946                ref cents_worth_used,
947                ref text_message,
948                ref created_timestamp,
949                ref donor,
950                ref recipient
951            } => {
952                &[]
953            },
954            Freeby::FFA {
955                ref id,
956                ref allowed_categories,
957                ref allowed_drinks,
958                ref allowed_number_total,
959                ref allowed_number_used,
960                ref text_message,
961                ref created_timestamp,
962                ref donor
963            } => {
964                allowed_drinks
965            },
966        };
967    }
968
969    fn left(&self) -> u16 {
970        return match *self {
971            Freeby::Classic {
972                ref id,
973                ref allowed_categories,
974                ref allowed_drinks,
975                ref allowed_number_total,
976                ref allowed_number_used,
977                ref text_message,
978                ref created_timestamp,
979                ref donor,
980                ref recipient
981            } => {
982                (*allowed_number_total) -  (*allowed_number_used)
983            },
984            Freeby::Transfer{
985                ref id,
986                ref cents_worth_total,
987                ref cents_worth_used,
988                ref text_message,
989                ref created_timestamp,
990                ref donor,
991                ref recipient
992            } => {
993                0u16
994            },
995            Freeby::FFA {
996                ref id,
997                ref allowed_categories,
998                ref allowed_drinks,
999                ref allowed_number_total,
1000                ref allowed_number_used,
1001                ref text_message,
1002                ref created_timestamp,
1003                ref donor
1004            } => {
1005                println!("total = {} & used = {}", allowed_number_total, allowed_number_used);
1006                (*allowed_number_total) -  (*allowed_number_used)
1007            },
1008        };
1009    }
1010    fn decrement(&mut self) -> () {
1011        return match *self {
1012            Freeby::Classic {
1013                ref id,
1014                ref allowed_categories,
1015                ref allowed_drinks,
1016                ref allowed_number_total,
1017                ref mut allowed_number_used,
1018                ref text_message,
1019                ref created_timestamp,
1020                ref donor,
1021                ref recipient
1022            } => {
1023                let old: u16 = *allowed_number_used;
1024                *allowed_number_used = old + 1;
1025            },
1026            Freeby::Transfer{
1027                ref id,
1028                ref cents_worth_total,
1029                ref cents_worth_used,
1030                ref text_message,
1031                ref created_timestamp,
1032                ref donor,
1033                ref recipient
1034            } => {
1035                unimplemented!();
1036            },
1037            Freeby::FFA {
1038                ref id,
1039                ref allowed_categories,
1040                ref allowed_drinks,
1041                ref allowed_number_total,
1042                ref mut allowed_number_used,
1043                ref text_message,
1044                ref created_timestamp,
1045                ref donor
1046            } => {
1047                let old: u16 = *allowed_number_used;
1048                *allowed_number_used = old + 1;
1049            },
1050        };
1051    }
1052    fn get_budget_cents_left(&self) -> u64 {
1053        return match *self {
1054            Freeby::Transfer{
1055                ref id,
1056                ref cents_worth_total,
1057                ref cents_worth_used,
1058                ref text_message,
1059                ref created_timestamp,
1060                ref donor,
1061                ref recipient
1062            } => {
1063                (*cents_worth_total - *cents_worth_used)
1064            },
1065            _ => panic!("Cannot get cents left for non-budget-freeby"),
1066        }
1067    }
1068
1069    fn remove_budget_by(&mut self, value: u64) {
1070        match *self {
1071            Freeby::Transfer{
1072                ref id,
1073                ref cents_worth_total,
1074                ref mut cents_worth_used,
1075                ref text_message,
1076                ref created_timestamp,
1077                ref donor,
1078                ref recipient
1079            } => {
1080                *cents_worth_used += value;
1081            },
1082            _ => {
1083                panic!("Cannot set cents left for non-budget-freeby");
1084            },
1085        }
1086    }
1087}
1088
1089
1090pub trait FreebyAble {
1091    fn message(&self) -> &str;
1092    fn get_id(&self) -> u64;
1093    fn get_donor(&self) -> u32;
1094    fn allowed_categories(&self) -> &[String];
1095    fn allowed_items(&self) -> &[u32];
1096    fn is_open(&self) -> bool {
1097        return FreebyAble::left(self) != 0;
1098    }
1099    fn decrement(&mut self) -> ();
1100    fn get_budget_cents_left(&self) -> u64;
1101    fn remove_budget_by(&mut self, value: u64);
1102    fn left(&self) -> u16;
1103    fn allows(&self, item_to_allow : &Item) -> bool {
1104        for id in self.allowed_items() {
1105            if *id == item_to_allow.item_id {
1106                return true;
1107            }
1108        }
1109        if let Some(ref cat_to) = item_to_allow.category {
1110            for cat in self.allowed_categories() {
1111                if cat.eq(cat_to) {
1112                    return true;
1113                }
1114            }
1115        }
1116        return false;
1117    }
1118}
1119
1120
1121
1122#[derive(Debug, Serialize, Deserialize, Clone, TypeScriptify)]
1123pub enum Purchase {
1124    FFAPurchase {
1125        unique_id: u64,
1126        timestamp_epoch_millis: i64,
1127        item_id: u32,
1128        freeby_id: u64,
1129        donor: u32,
1130    },
1131    //UndoPurchase { unique_id: u64 }, //deletes instance directly from purchases
1132    SimplePurchase {
1133        unique_id: u64,
1134        timestamp_epoch_millis: i64,
1135        item_id: u32, //buys one instance of this item
1136        consumer_id: u32,
1137    },
1138    SpecialPurchase {
1139        unique_id: u64,
1140        timestamp_epoch_millis: i64,
1141        special_name: String,
1142        specialcost: Option<u32>, //set to None, set to correct value during bill finalization
1143        consumer_id: u32,
1144    },
1145}
1146
1147
1148impl PurchaseFunctions for Purchase {
1149    fn get_unique_id(&self) -> u64 {
1150        match self {
1151            &Purchase::SpecialPurchase{
1152                ref unique_id,
1153                ref timestamp_epoch_millis,
1154                ref special_name,
1155                ref specialcost,
1156                ref consumer_id,
1157            } => {
1158                return *unique_id;
1159            },
1160            &Purchase::SimplePurchase  {
1161                ref unique_id,
1162                ref timestamp_epoch_millis,
1163                ref item_id,
1164                ref consumer_id,
1165            } => {
1166                return *unique_id;
1167            },
1168            &Purchase::FFAPurchase {
1169                ref unique_id,
1170                ref timestamp_epoch_millis,
1171                ref item_id,
1172                ref freeby_id,
1173                ref donor,
1174            } => {
1175                return *unique_id;
1176            },
1177        }
1178    }
1179
1180    fn get_special_set_price(&self) -> Option<u32> {
1181        match self {
1182            &Purchase::SpecialPurchase {
1183                ref unique_id,
1184                ref timestamp_epoch_millis,
1185                ref special_name,
1186                ref specialcost,
1187                ref consumer_id,
1188            } => return *specialcost,
1189            _ => panic!("get_special_set_price called on non-special purchase"),
1190        }
1191    }
1192
1193    fn has_unique_id(&self, other: u64) -> bool {
1194        match self {
1195            &Purchase::SpecialPurchase{
1196                ref unique_id,
1197                ref timestamp_epoch_millis,
1198                ref special_name,
1199                ref specialcost,
1200                ref consumer_id,
1201            } => {
1202                return *unique_id == other;
1203            },
1204            &Purchase::SimplePurchase {
1205                ref unique_id,
1206                ref timestamp_epoch_millis,
1207                ref item_id,
1208                ref consumer_id,
1209            } => {
1210                return *unique_id == other;
1211            },
1212            &Purchase::FFAPurchase {
1213                ref unique_id,
1214                ref timestamp_epoch_millis,
1215                ref item_id,
1216                ref freeby_id,
1217                ref donor,
1218            } => {
1219                return *unique_id == other;
1220            }
1221        }
1222    }
1223
1224    fn has_user_id(&self) -> bool {
1225        match self {
1226            &Purchase::SpecialPurchase{
1227                ref unique_id,
1228                ref timestamp_epoch_millis,
1229                ref special_name,
1230                ref specialcost,
1231                ref consumer_id,
1232            } => {
1233                return true;
1234            },
1235            &Purchase::SimplePurchase {
1236                ref unique_id,
1237                ref timestamp_epoch_millis,
1238                ref item_id,
1239                ref consumer_id,
1240            } => {
1241                return true;
1242            },
1243            &Purchase::FFAPurchase {
1244                ref unique_id,
1245                ref timestamp_epoch_millis,
1246                ref item_id,
1247                ref freeby_id,
1248                ref donor,
1249            } => {
1250                return true;
1251            }
1252        }
1253    }
1254    fn get_user_id(&self) -> &u32 {
1255        match self {
1256            &Purchase::SpecialPurchase{
1257                ref unique_id,
1258                ref timestamp_epoch_millis,
1259                ref special_name,
1260                ref specialcost,
1261                ref consumer_id,
1262            } => {
1263                return consumer_id;
1264            },
1265            &Purchase::SimplePurchase {
1266                ref unique_id,
1267                ref timestamp_epoch_millis,
1268                ref item_id,
1269                ref consumer_id,
1270            } => {
1271                return consumer_id;
1272            },
1273            &Purchase::FFAPurchase {
1274                ref unique_id,
1275                ref timestamp_epoch_millis,
1276                ref item_id,
1277                ref freeby_id,
1278                ref donor,
1279            } => {
1280                return donor;
1281            }
1282        }
1283    }
1284
1285    fn get_item_id(&self) -> &u32 {
1286        match self {
1287            &Purchase::SpecialPurchase{
1288                ref unique_id,
1289                ref timestamp_epoch_millis,
1290                ref special_name,
1291                ref specialcost,
1292                ref consumer_id,
1293            } => {
1294                panic!("Cannot get item_id of a special purchase");
1295            },
1296            &Purchase::SimplePurchase {
1297                ref unique_id,
1298                ref timestamp_epoch_millis,
1299                ref item_id,
1300                ref consumer_id,
1301            } => {
1302                return item_id;
1303            },
1304            &Purchase::FFAPurchase {
1305                ref unique_id,
1306                ref timestamp_epoch_millis,
1307                ref item_id,
1308                ref freeby_id,
1309                ref donor,
1310            } => {
1311                return item_id;
1312            }
1313        }
1314    }
1315
1316    fn get_timestamp(&self) -> &i64 {
1317        match self {
1318            &Purchase::SpecialPurchase{
1319                ref unique_id,
1320                ref timestamp_epoch_millis,
1321                ref special_name,
1322                ref specialcost,
1323                ref consumer_id,
1324            } => {
1325                return timestamp_epoch_millis;
1326            },
1327            &Purchase::SimplePurchase {
1328                ref unique_id,
1329                ref timestamp_epoch_millis,
1330                ref item_id,
1331                ref consumer_id,
1332            } => {
1333                return timestamp_epoch_millis;
1334            },
1335            &Purchase::FFAPurchase {
1336                ref unique_id,
1337                ref timestamp_epoch_millis,
1338                ref item_id,
1339                ref freeby_id,
1340                ref donor,
1341            } => {
1342                return timestamp_epoch_millis;
1343            }
1344        }
1345    }
1346    fn has_item_id(&self) -> bool {
1347        match self {
1348            a @ &Purchase::SpecialPurchase {
1349                ..
1350            } => false,
1351            _ => true,
1352        }
1353    }
1354
1355    fn is_special(&self) -> bool {
1356        match self {
1357            a @ &Purchase::SpecialPurchase {
1358                ..
1359            } => true,
1360            _ => false,
1361        }
1362    }
1363}