1#![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 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>, pub open_freebies: HashMap<u32, Vec<Freeby>>, pub open_ffa: Vec<Freeby>,
90
91 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 touched_users_set.insert(uid);
252 let usr = self.users.get(&uid).unwrap();
253
254 if !usr.is_billed {
255 } else if bill.users_that_will_not_be_billed.contains(&uid) {
257 } else if usr.external_user_id.is_none() {
259 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 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
403pub 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#[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 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#[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 pub timestamp_from: i64,
650 pub timestamp_to: i64,
651 pub comment: String,
652 pub users: UserGroup,
653 pub bill_state: BillState,
654
655 pub users_that_will_not_be_billed: HashSet<u32>,
657
658 pub finalized_data : ExportableBillData,
660
661}
662
663impl Bill {
664 pub fn get_day_index(&self, time: i64) -> usize {
665 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
725impl 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 SimplePurchase {
1133 unique_id: u64,
1134 timestamp_epoch_millis: i64,
1135 item_id: u32, consumer_id: u32,
1137 },
1138 SpecialPurchase {
1139 unique_id: u64,
1140 timestamp_epoch_millis: i64,
1141 special_name: String,
1142 specialcost: Option<u32>, 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}