use std::collections::HashSet;
use super::{BonusTrack, PushContext, Reward, Tier};
pub struct MultipleRepos;
static TIERS: &[Tier] = &[
Tier {
cost: 50,
reward: Reward::Multiplier(2),
},
Tier {
cost: 500,
reward: Reward::Multiplier(3),
},
Tier {
cost: 3000,
reward: Reward::Multiplier(4),
},
Tier {
cost: 20000,
reward: Reward::Multiplier(5),
},
Tier {
cost: 120000,
reward: Reward::Multiplier(6),
},
];
impl BonusTrack for MultipleRepos {
fn id(&self) -> &'static str {
"multiple_repos"
}
fn name(&self) -> &'static str {
"Spread the Love"
}
fn description(&self) -> &'static str {
"Multiplier for each time you push to a different repo today (after the first)."
}
fn tiers(&self) -> &'static [Tier] {
TIERS
}
fn applies(&self, ctx: &PushContext) -> u32 {
if ctx.push.commits().is_empty() {
return 0;
}
let today = ctx.clock.today_id();
let repos_pushed_today: HashSet<String> = ctx
.history
.entries_since(ctx.clock.today_start())
.unwrap_or_default()
.into_iter()
.filter(|e| ctx.clock.day_id_of(e.timestamp()) == today)
.map(|e| e.remote_url().to_string())
.collect();
let is_new_repo = !repos_pushed_today.contains(ctx.push.remote_url());
let has_pushed_before_today = !repos_pushed_today.is_empty();
if is_new_repo && has_pushed_before_today {
1
} else {
0
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
bonus_track::Clock,
git::{Commit, Push},
storage::{DbConnection, PushEntry, PushHistory},
};
const SECONDS_PER_DAY: u64 = 86400;
fn clock_at_day(day: u64) -> Clock {
Clock::at(day * SECONDS_PER_DAY + 3600)
}
fn timestamp_on_day(day: u64) -> u64 {
day * SECONDS_PER_DAY + 3600
}
#[test]
fn applies_on_second_repo() {
let conn = DbConnection::create_in_memory().unwrap();
let bonus = MultipleRepos;
let clock = clock_at_day(100);
let history = PushHistory::new(&conn).with_entries([PushEntry::with_repo(
timestamp_on_day(100),
"git@github.com:user/repo1.git",
)]);
let push = Push::with_repo(vec![Commit::default()], "git@github.com:user/repo2.git");
let ctx = PushContext {
push: &push,
history: &history,
clock: &clock,
};
assert_eq!(bonus.applies(&ctx), 1);
}
#[test]
fn applies_on_third_repo() {
let conn = DbConnection::create_in_memory().unwrap();
let bonus = MultipleRepos;
let clock = clock_at_day(100);
let history = PushHistory::new(&conn).with_entries([
PushEntry::with_repo(timestamp_on_day(100), "git@github.com:user/repo1.git"),
PushEntry::with_repo(timestamp_on_day(100), "git@github.com:user/repo2.git"),
]);
let push = Push::with_repo(vec![Commit::default()], "git@github.com:user/repo3.git");
let ctx = PushContext {
push: &push,
history: &history,
clock: &clock,
};
assert_eq!(bonus.applies(&ctx), 1);
}
#[test]
fn does_not_apply_on_first_repo() {
let conn = DbConnection::create_in_memory().unwrap();
let bonus = MultipleRepos;
let clock = clock_at_day(100);
let history = PushHistory::new(&conn);
let push = Push::with_repo(vec![Commit::default()], "git@github.com:user/repo1.git");
let ctx = PushContext {
push: &push,
history: &history,
clock: &clock,
};
assert_eq!(bonus.applies(&ctx), 0);
}
#[test]
fn does_not_apply_on_repeat_push_to_same_repo() {
let conn = DbConnection::create_in_memory().unwrap();
let bonus = MultipleRepos;
let clock = clock_at_day(100);
let history = PushHistory::new(&conn).with_entries([PushEntry::with_repo(
timestamp_on_day(100),
"git@github.com:user/repo1.git",
)]);
let push = Push::with_repo(vec![Commit::default()], "git@github.com:user/repo1.git");
let ctx = PushContext {
push: &push,
history: &history,
clock: &clock,
};
assert_eq!(bonus.applies(&ctx), 0);
}
#[test]
fn does_not_count_repos_from_other_days() {
let conn = DbConnection::create_in_memory().unwrap();
let bonus = MultipleRepos;
let clock = clock_at_day(100);
let history = PushHistory::new(&conn).with_entries([
PushEntry::with_repo(timestamp_on_day(99), "git@github.com:user/repo1.git"),
PushEntry::with_repo(timestamp_on_day(99), "git@github.com:user/repo2.git"),
]);
let push = Push::with_repo(vec![Commit::default()], "git@github.com:user/repo3.git");
let ctx = PushContext {
push: &push,
history: &history,
clock: &clock,
};
assert_eq!(bonus.applies(&ctx), 0);
}
#[test]
fn does_not_apply_to_empty_pushes() {
let conn = DbConnection::create_in_memory().unwrap();
let bonus = MultipleRepos;
let clock = clock_at_day(100);
let history = PushHistory::new(&conn).with_entries([PushEntry::with_repo(
timestamp_on_day(100),
"git@github.com:user/repo1.git",
)]);
let push = Push::with_repo(vec![], "git@github.com:user/repo2.git");
let ctx = PushContext {
push: &push,
history: &history,
clock: &clock,
};
assert_eq!(bonus.applies(&ctx), 0);
}
}