use core::time::Duration;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum WaniKaniStage {
Apprentice1,
Apprentice2,
Apprentice3,
Apprentice4,
Guru1,
Guru2,
Master,
Enlightened,
Burned,
}
impl WaniKaniStage {
pub fn srs_penalty_factor(&self) -> u32 {
use WaniKaniStage::*;
match self {
Apprentice1 |
Apprentice2 |
Apprentice3 |
Apprentice4 => 1,
_ => 2,
}
}
pub fn interval(&self) -> Duration {
const HOUR: Duration = Duration::from_secs(60*60);
const DAY: Duration = Duration::from_secs(60*60*24);
const MONTH: Duration = Duration::from_secs(60*60*24*30);
match self {
WaniKaniStage::Apprentice1 => 4 * HOUR,
WaniKaniStage::Apprentice2 => 8 * HOUR,
WaniKaniStage::Apprentice3 => DAY,
WaniKaniStage::Apprentice4 => 2 * DAY,
WaniKaniStage::Guru1 => 7 * DAY,
WaniKaniStage::Guru2 => 14 * DAY,
WaniKaniStage::Master => MONTH,
WaniKaniStage::Enlightened => 4 * MONTH,
WaniKaniStage::Burned => Duration::MAX,
}
}
pub fn from_stage_num(stage: u32) -> WaniKaniStage {
match stage {
0 => WaniKaniStage::Apprentice1,
1 => WaniKaniStage::Apprentice2,
2 => WaniKaniStage::Apprentice3,
3 => WaniKaniStage::Apprentice4,
4 => WaniKaniStage::Guru1,
5 => WaniKaniStage::Guru2,
6 => WaniKaniStage::Master,
7 => WaniKaniStage::Enlightened,
_ => WaniKaniStage::Burned,
}
}
pub fn stage_num(&self) -> u32 {
match self {
WaniKaniStage::Apprentice1 => 0,
WaniKaniStage::Apprentice2 => 1,
WaniKaniStage::Apprentice3 => 2,
WaniKaniStage::Apprentice4 => 3,
WaniKaniStage::Guru1 => 4,
WaniKaniStage::Guru2 => 5,
WaniKaniStage::Master => 6,
WaniKaniStage::Enlightened => 7,
WaniKaniStage::Burned => 8,
}
}
pub fn after_adjustment(&self, amt: i32) -> WaniKaniStage {
let new_stage_num = ((self.stage_num() as i32) + amt).max(0) as u32;
WaniKaniStage::from_stage_num(new_stage_num)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct WanikaniItem {
pub stage: WaniKaniStage,
pub incorrect_streak: u32,
}
impl Default for WanikaniItem {
fn default() -> Self {
WanikaniItem {
stage: WaniKaniStage::Apprentice1,
incorrect_streak: 0
}
}
}
impl WanikaniItem {
pub fn new(stage: WaniKaniStage, incorrect_streak: u32) -> Self {
WanikaniItem {
stage,
incorrect_streak,
}
}
pub fn review(&self, correct: bool) -> WanikaniItem {
wanikani_review(correct, &self)
}
}
pub fn wanikani_review(correct: bool, item: &WanikaniItem) -> WanikaniItem {
if correct {
WanikaniItem {
stage: item.stage.after_adjustment(1),
incorrect_streak: 0,
}
} else {
let incorrect_streak = item.incorrect_streak + 1;
let stage_change = 0 - (incorrect_streak * item.stage.srs_penalty_factor()) as i32;
WanikaniItem {
stage: item.stage.after_adjustment(stage_change),
incorrect_streak,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test1() {
assert_eq!(
WanikaniItem::default().review(true),
WanikaniItem::new(WaniKaniStage::Apprentice2, 0),
);
assert_eq!(
WanikaniItem::new(WaniKaniStage::Apprentice2, 0).review(true),
WanikaniItem::new(WaniKaniStage::Apprentice3, 0),
);
assert_eq!(
WanikaniItem::new(WaniKaniStage::Apprentice3, 0).review(true),
WanikaniItem::new(WaniKaniStage::Apprentice4, 0),
);
assert_eq!(
WanikaniItem::new(WaniKaniStage::Apprentice4, 0).review(true),
WanikaniItem::new(WaniKaniStage::Guru1, 0),
);
assert_eq!(
WanikaniItem::new(WaniKaniStage::Guru1, 0).review(true),
WanikaniItem::new(WaniKaniStage::Guru2, 0),
);
assert_eq!(
WanikaniItem::new(WaniKaniStage::Guru2, 0).review(true),
WanikaniItem::new(WaniKaniStage::Master, 0),
);
assert_eq!(
WanikaniItem::new(WaniKaniStage::Master, 0).review(true),
WanikaniItem::new(WaniKaniStage::Enlightened, 0),
);
assert_eq!(
WanikaniItem::new(WaniKaniStage::Enlightened, 0).review(true),
WanikaniItem::new(WaniKaniStage::Burned, 0),
);
assert_eq!(
WanikaniItem::new(WaniKaniStage::Burned, 0).review(true),
WanikaniItem::new(WaniKaniStage::Burned, 0),
);
}
#[test]
fn test2() {
assert_eq!(
WanikaniItem::default().review(false),
WanikaniItem::new(WaniKaniStage::Apprentice1, 1),
);
assert_eq!(
WanikaniItem::new(WaniKaniStage::Apprentice2, 0).review(false),
WanikaniItem::new(WaniKaniStage::Apprentice1, 1),
);
assert_eq!(
WanikaniItem::new(WaniKaniStage::Apprentice3, 0).review(false),
WanikaniItem::new(WaniKaniStage::Apprentice2, 1),
);
assert_eq!(
WanikaniItem::new(WaniKaniStage::Apprentice4, 0).review(false),
WanikaniItem::new(WaniKaniStage::Apprentice3, 1),
);
assert_eq!(
WanikaniItem::new(WaniKaniStage::Guru1, 0).review(false),
WanikaniItem::new(WaniKaniStage::Apprentice3, 1),
);
assert_eq!(
WanikaniItem::new(WaniKaniStage::Guru2, 0).review(false),
WanikaniItem::new(WaniKaniStage::Apprentice4, 1),
);
assert_eq!(
WanikaniItem::new(WaniKaniStage::Master, 0).review(false),
WanikaniItem::new(WaniKaniStage::Guru1, 1),
);
assert_eq!(
WanikaniItem::new(WaniKaniStage::Enlightened, 0).review(false),
WanikaniItem::new(WaniKaniStage::Guru2, 1),
);
}
}