use std::collections::HashMap;
use crate::error::Fallible;
use crate::error::fail;
use crate::types::card_hash::CardHash;
use crate::types::performance::Performance;
pub struct Cache {
changes: HashMap<CardHash, Performance>,
}
impl Cache {
pub fn new() -> Self {
Self {
changes: HashMap::new(),
}
}
pub fn insert(&mut self, card_hash: CardHash, performance: Performance) -> Fallible<()> {
match self.changes.get(&card_hash) {
Some(_) => fail(format!("Card with hash {card_hash} already in cache")),
None => {
self.changes.insert(card_hash, performance);
Ok(())
}
}
}
pub fn get(&self, card_hash: CardHash) -> Fallible<Performance> {
match self.changes.get(&card_hash) {
Some(performance) => Ok(*performance),
None => fail(format!("Card with hash {card_hash} not found in cache")),
}
}
pub fn update(&mut self, card_hash: CardHash, performance: Performance) -> Fallible<()> {
match self.changes.get_mut(&card_hash) {
Some(p) => {
*p = performance;
Ok(())
}
None => fail(format!("Card with hash {card_hash} not found in cache")),
}
}
pub fn iter(&self) -> impl Iterator<Item = (&CardHash, &Performance)> {
self.changes.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::fail;
use crate::types::date::Date;
use crate::types::performance::ReviewedPerformance;
use crate::types::timestamp::Timestamp;
#[test]
fn test_cache_insert_and_get() -> Fallible<()> {
let mut cache = Cache::new();
let card_hash = CardHash::hash_bytes(b"a");
let performance = Performance::New;
cache.insert(card_hash, performance)?;
let retrieved = cache.get(card_hash)?;
match retrieved {
Performance::New => Ok(()),
_ => fail("Expected Performance::New"),
}
}
#[test]
fn test_cache_update() -> Fallible<()> {
let mut cache = Cache::new();
let card_hash = CardHash::hash_bytes(b"a");
let performance = Performance::New;
cache.insert(card_hash, performance)?;
let last_reviewed_at = Timestamp::now();
let stability = 1.0;
let difficulty = 2.0;
let interval_raw = 0.4;
let interval_days = 1;
let due_date = Date::today();
let review_count = 3;
cache.update(
card_hash,
Performance::Reviewed(ReviewedPerformance {
last_reviewed_at,
stability,
difficulty,
interval_raw,
interval_days,
due_date,
review_count,
}),
)?;
let retrieved = cache.get(card_hash)?;
match retrieved {
Performance::Reviewed(rp) => {
assert_eq!(rp.last_reviewed_at, last_reviewed_at);
assert_eq!(rp.stability, stability);
assert_eq!(rp.difficulty, difficulty);
assert_eq!(rp.interval_raw, 0.4);
assert_eq!(rp.interval_days, interval_days);
assert_eq!(rp.due_date, due_date);
assert_eq!(rp.review_count, review_count);
Ok(())
}
_ => fail("Expected Performance::Reviewed"),
}
}
#[test]
fn test_cache_insert_duplicate() -> Fallible<()> {
let mut cache = Cache::new();
let card_hash = CardHash::hash_bytes(b"a");
let performance = Performance::New;
cache.insert(card_hash, performance)?;
assert!(cache.insert(card_hash, performance).is_err());
Ok(())
}
#[test]
fn test_cache_get_nonexistent() -> Fallible<()> {
let cache = Cache::new();
let card_hash = CardHash::hash_bytes(b"a");
assert!(cache.get(card_hash).is_err());
Ok(())
}
#[test]
fn test_cache_update_nonexistent() -> Fallible<()> {
let mut cache = Cache::new();
let card_hash = CardHash::hash_bytes(b"a");
let last_reviewed_at = Timestamp::now();
let stability = 1.0;
let difficulty = 2.0;
let interval_raw = 0.4;
let interval_days = 1;
let due_date = Date::today();
let review_count = 3;
let reviewed = Performance::Reviewed(ReviewedPerformance {
last_reviewed_at,
stability,
difficulty,
interval_raw,
interval_days,
due_date,
review_count,
});
let res = cache.update(card_hash, reviewed);
assert!(res.is_err());
Ok(())
}
#[test]
fn test_cache_iter() -> Fallible<()> {
let mut cache = Cache::new();
let card_hash = CardHash::hash_bytes(b"a");
let performance = Performance::New;
cache.insert(card_hash, performance)?;
let mut iter = cache.iter();
let (key, value) = iter.next().unwrap();
assert_eq!(*key, card_hash);
assert!(value.is_new());
assert!(iter.next().is_none());
Ok(())
}
}