use crate::error::Result;
use crate::types::{CorrectionEntry, CorrectionId, CorrectionLog};
pub struct ContextWindow {
pub entries: Vec<String>,
}
impl ContextWindow {
pub fn new() -> Self {
Self { entries: Vec::new() }
}
}
impl Default for ContextWindow {
fn default() -> Self {
Self::new()
}
}
impl CorrectionLog {
pub fn new() -> Self {
Self { entries: Vec::new() }
}
pub fn append(&mut self, entry: CorrectionEntry) -> CorrectionId {
let id = entry.id.clone();
self.entries.push(entry);
id
}
pub fn entries(&self) -> &[CorrectionEntry] {
&self.entries
}
pub fn replay_into(&self, context: &mut ContextWindow) -> Result<()> {
for entry in &self.entries {
context.entries.push(entry.correction.clone());
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::CorrectionEntry;
use chrono::Utc;
use proptest::prelude::*;
fn make_entry(id: &str, correction: &str) -> CorrectionEntry {
CorrectionEntry {
id: id.to_string(),
timestamp: Utc::now(),
original: "original".to_string(),
correction: correction.to_string(),
context: "ctx".to_string(),
}
}
#[test]
fn new_log_is_empty() {
let log = CorrectionLog::new();
assert!(log.entries().is_empty());
}
#[test]
fn append_returns_entry_id() {
let mut log = CorrectionLog::new();
let id = log.append(make_entry("id-1", "use const"));
assert_eq!(id, "id-1");
}
#[test]
fn entries_are_immutable_slice() {
let mut log = CorrectionLog::new();
log.append(make_entry("a", "fix a"));
log.append(make_entry("b", "fix b"));
let entries = log.entries();
assert_eq!(entries.len(), 2);
assert_eq!(entries[0].id, "a");
assert_eq!(entries[1].id, "b");
}
#[test]
fn replay_into_pushes_corrections() {
let mut log = CorrectionLog::new();
log.append(make_entry("1", "use const"));
log.append(make_entry("2", "no var"));
let mut ctx = ContextWindow::new();
log.replay_into(&mut ctx).unwrap();
assert_eq!(ctx.entries, vec!["use const", "no var"]);
}
#[test]
fn replay_into_empty_log_is_noop() {
let log = CorrectionLog::new();
let mut ctx = ContextWindow::new();
log.replay_into(&mut ctx).unwrap();
assert!(ctx.entries.is_empty());
}
fn arb_entry() -> impl Strategy<Value = CorrectionEntry> {
("[a-z]{1,8}", "[a-z ]{1,20}", "[a-z ]{1,20}", "[a-z ]{1,20}").prop_map(
|(id, original, correction, context)| CorrectionEntry {
id,
timestamp: Utc::now(),
original,
correction,
context,
},
)
}
#[test]
fn prop17_correction_log_immutability() {
proptest!(|(entries in proptest::collection::vec(arb_entry(), 1..=20))| {
let mut log = CorrectionLog::new();
let mut snapshots: Vec<(String, String, String, String)> = Vec::new();
for entry in entries {
let id = entry.id.clone();
let original = entry.original.clone();
let correction = entry.correction.clone();
let context = entry.context.clone();
log.append(entry);
snapshots.push((id, original, correction, context));
let current = log.entries();
prop_assert_eq!(current.len(), snapshots.len());
for (i, (exp_id, exp_orig, exp_corr, exp_ctx)) in snapshots.iter().enumerate() {
prop_assert_eq!(¤t[i].id, exp_id);
prop_assert_eq!(¤t[i].original, exp_orig);
prop_assert_eq!(¤t[i].correction, exp_corr);
prop_assert_eq!(¤t[i].context, exp_ctx);
}
}
});
}
#[test]
fn prop18_compaction_preserves_correction_log() {
proptest!(|(entries in proptest::collection::vec(arb_entry(), 0..=20))| {
let mut original_log = CorrectionLog::new();
for entry in &entries {
original_log.append(CorrectionEntry {
id: entry.id.clone(),
timestamp: entry.timestamp,
original: entry.original.clone(),
correction: entry.correction.clone(),
context: entry.context.clone(),
});
}
let n = original_log.entries().len();
let mut compacted_log = CorrectionLog::new();
for entry in original_log.entries() {
compacted_log.append(CorrectionEntry {
id: entry.id.clone(),
timestamp: entry.timestamp,
original: entry.original.clone(),
correction: entry.correction.clone(),
context: entry.context.clone(),
});
}
prop_assert_eq!(compacted_log.entries().len(), n);
for (i, orig) in original_log.entries().iter().enumerate() {
let comp = &compacted_log.entries()[i];
prop_assert_eq!(&comp.id, &orig.id);
prop_assert_eq!(&comp.original, &orig.original);
prop_assert_eq!(&comp.correction, &orig.correction);
prop_assert_eq!(&comp.context, &orig.context);
}
});
}
}