use crate::Position;
use crate::event::EventRef;
use super::ActiveTail;
#[derive(Debug)]
pub struct Rebuilt {
pub index: ActiveTail,
pub count: u64,
pub unindexable: bool,
}
#[derive(Debug)]
pub struct Rebuilder {
index: ActiveTail,
count: u64,
unindexable: bool,
}
impl Rebuilder {
pub fn new(base: Position) -> Self {
Rebuilder {
index: ActiveTail::new(base),
count: 0,
unindexable: false,
}
}
pub fn feed(&mut self, position: Position, event: EventRef<'_>) {
self.count += 1;
if self.unindexable {
return;
}
if self.index.push(position, event).is_err() {
self.unindexable = true;
}
}
pub fn finish(self) -> Rebuilt {
Rebuilt {
index: self.index,
count: self.count,
unindexable: self.unindexable,
}
}
}
pub fn rebuild<'a>(
base: Position,
records: impl Iterator<Item = (Position, EventRef<'a>)>,
) -> Rebuilt {
let mut builder = Rebuilder::new(base);
for (position, event) in records {
builder.feed(position, event);
}
builder.finish()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::event::{Event, EventType, Tag, Tags};
use crate::index::search;
use crate::query::{Query, QueryItem};
use smallvec::SmallVec;
fn tags(items: &[&str]) -> Tags {
Tags::new(
items
.iter()
.map(|s| Tag::new(s).unwrap())
.collect::<SmallVec<[Tag; 4]>>(),
)
.unwrap()
}
fn event(ty: &str, tag_strs: &[&str]) -> Event {
Event::new(&EventType::new(ty).unwrap(), &tags(tag_strs), b"").unwrap()
}
#[test]
fn rebuild_reconstructs_the_same_index() {
let events = [
event("Registered", &[]),
event("Enrolled", &["course:c1"]),
event("Enrolled", &["course:c1", "student:s1"]),
];
let base = Position::new(1);
let rebuilt = rebuild(
base,
events
.iter()
.enumerate()
.map(|(i, ev)| (Position::new(1 + i as u64), ev.as_ref())),
);
assert_eq!(rebuilt.count, 3);
assert!(!rebuilt.unindexable);
let q = Query::item(QueryItem::with_tags(tags(&["course:c1"])));
let got: Vec<Position> = search(&rebuilt.index.view_full(), &q, Position::ZERO).collect();
assert_eq!(got, vec![Position::new(2), Position::new(3)]);
}
#[test]
fn rebuilder_counts_and_feeds_incrementally() {
let mut builder = Rebuilder::new(Position::new(10));
let a = event("E", &["a"]);
let b = event("E", &["b"]);
builder.feed(Position::new(10), a.as_ref());
builder.feed(Position::new(11), b.as_ref());
let rebuilt = builder.finish();
assert_eq!(rebuilt.count, 2);
assert_eq!(rebuilt.index.base(), Position::new(10));
assert_eq!(rebuilt.index.len(), 2);
}
#[test]
fn empty_range_rebuilds_to_empty() {
let rebuilt = rebuild(Position::new(1), std::iter::empty());
assert_eq!(rebuilt.count, 0);
assert!(!rebuilt.unindexable);
assert!(rebuilt.index.is_empty());
}
}