use std::time::Duration;
use crate::time::Instant;
pub const COMMIT_CHAIN_PAUSE_SECONDS: u64 = 8;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Pick {
pub canonical_pinyin: String,
pub word: String,
pub committed_at: Instant,
}
#[derive(Debug, Clone, Default)]
pub struct CommitChain {
pub previous: Option<String>,
pub earlier: Option<String>,
pub same_composition: bool,
pub committed_at: Option<Instant>,
pub last_pick: Option<Pick>,
}
impl CommitChain {
pub fn reset(&mut self) {
self.previous = None;
self.earlier = None;
self.same_composition = false;
self.last_pick = None;
}
pub fn advance(&mut self, word: &str, composition_left: bool, now: Instant) {
self.earlier = self.previous.take();
self.previous = Some(word.to_owned());
self.same_composition = composition_left;
self.committed_at = Some(now);
}
pub fn paused(&self, now: Instant) -> bool {
self.committed_at.is_none_or(|at| {
now.saturating_duration_since(at) > Duration::from_secs(COMMIT_CHAIN_PAUSE_SECONDS)
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn advance_shifts_the_context_and_stamps_the_time() {
let now = Instant::now();
let mut chain = CommitChain::default();
chain.advance("你好", false, now);
chain.advance("世界", true, now + Duration::from_secs(1));
assert_eq!(chain.previous.as_deref(), Some("世界"));
assert_eq!(chain.earlier.as_deref(), Some("你好"));
assert!(chain.same_composition);
assert_eq!(chain.committed_at, Some(now + Duration::from_secs(1)));
}
#[test]
fn reset_keeps_only_the_commit_time() {
let now = Instant::now();
let mut chain = CommitChain::default();
chain.advance("你", true, now);
chain.last_pick = Some(Pick {
canonical_pinyin: "ni".to_owned(),
word: "你".to_owned(),
committed_at: now,
});
chain.reset();
assert!(chain.previous.is_none() && chain.earlier.is_none());
assert!(!chain.same_composition && chain.last_pick.is_none());
assert_eq!(chain.committed_at, Some(now));
}
#[test]
fn pause_is_strictly_longer_than_eight_seconds() {
let now = Instant::now();
let mut chain = CommitChain::default();
assert!(chain.paused(now));
chain.advance("你", false, now);
assert!(!chain.paused(now + Duration::from_secs(8)));
assert!(chain.paused(now + Duration::from_millis(8_001)));
assert!(!chain.paused(now - Duration::from_secs(1)));
}
}