1use patterns_macros::because;
2use std::collections::{BTreeMap, BTreeSet};
3
4#[derive(patterns_macros::Record)]
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
6pub struct Award {
7 pub count: u64,
8 pub first: u64,
9 pub last: u64,
10}
11because!(Award, "how many have earned one badge and when the first and the last did, the row a badge's leaderboard reads");
12
13#[derive(patterns_macros::Record)]
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct Earned<K, A> {
16 pub who: K,
17 pub badge: A,
18 pub at: u64,
19 pub rank: u64,
20}
21because!(Earned, "one account earning one badge at a moment, ranked by how many had earned it before, the row an account page lists");
22
23pub type Given<K, A> = BTreeSet<(K, A)>;
24because!(Given, "who has already been given which award, kept as a set of pairs so the question a grant asks, whether this one has had this already, is the set own membership test");
25
26pub type Awards<A> = BTreeMap<A, Award>;
27because!(Awards, "how many of each award have been given and when the first and last went out, which is what a rank is counted from");
28
29pub type Granted<K, A> = (Given<K, A>, Awards<A>, Vec<Earned<K, A>>);
30because!(Granted, "what a round of granting leaves behind: the pairs now given, the counts now raised, and the rows the round produced, returned together because a caller that took one and not the others would be holding a record of a grant that never happened");
31
32pub fn awarded<K: Ord + Clone, A: Ord + Copy>(given: Given<K, A>, awards: Awards<A>, candidates: &[(K, A)], at: u64) -> Granted<K, A> {
33 let mut given = given;
34 let mut awards = awards;
35 let mut rows = Vec::new();
36 for (who, badge) in candidates {
37 if !given.insert((who.clone(), *badge)) {
38 continue;
39 }
40 let award = awards.entry(*badge).or_default();
41 award.count += 1;
42 if award.first == 0 {
43 award.first = at;
44 }
45 award.last = award.last.max(at);
46 rows.push(Earned {
47 who: who.clone(),
48 badge: *badge,
49 at,
50 rank: award.count,
51 });
52 }
53 (given, awards, rows)
54}
55because!(awarded, "candidates granted once each and never again, each taking the next rank of its badge, in the order offered, so a badge earned twice in one batch counts once and the rank an account keeps is the one it earned at the time");