use super::super::WarpTableRow;
use super::{
MaintainedView, ObservedTable, ViewCreationError, ViewDefinition, ViewId, ViewRefreshError,
ViewRevision,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CountPublication {
pub through: ViewRevision,
pub count: usize,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CountRefreshOutcome {
Unchanged,
Published(CountPublication),
}
pub struct MaintainedCount<
Row: WarpTableRow,
Selection: ViewDefinition<Row, Value = ()>,
Brand = Row,
> {
membership: MaintainedView<Row, Selection, Brand>,
published: CountPublication,
}
impl<Row: WarpTableRow, Brand> ObservedTable<Row, Brand> {
pub fn maintain_count<Selection: ViewDefinition<Row, Value = ()>>(
&self,
id: ViewId,
selection: Selection,
) -> Result<MaintainedCount<Row, Selection, Brand>, ViewCreationError> {
let membership = self.maintain(id, selection)?;
let published = CountPublication {
through: membership.revision(),
count: membership.len(),
};
Ok(MaintainedCount {
membership,
published,
})
}
}
impl<Row: WarpTableRow, Selection: ViewDefinition<Row, Value = ()>, Brand>
MaintainedCount<Row, Selection, Brand>
{
pub const fn publication(&self) -> CountPublication {
self.published
}
pub fn refresh(
&mut self,
source: &ObservedTable<Row, Brand>,
) -> Result<CountRefreshOutcome, ViewRefreshError> {
let Some(revision) = self.published.through.revision.checked_add(1) else {
return Err(ViewRefreshError::RevisionExhausted);
};
self.membership.refresh(source)?;
let count = self.membership.len();
if count == self.published.count {
return Ok(CountRefreshOutcome::Unchanged);
}
self.published.count = count;
self.published.through.revision = revision;
Ok(CountRefreshOutcome::Published(self.published))
}
pub fn replace_selection(
&mut self,
source: &ObservedTable<Row, Brand>,
selection: Selection,
) -> Result<CountRefreshOutcome, (Selection, ViewRefreshError)> {
self.membership.replace_definition(source, selection)?;
self.published = CountPublication {
through: self.membership.revision(),
count: self.membership.len(),
};
Ok(CountRefreshOutcome::Published(self.published))
}
}
#[cfg(test)]
mod tests {
use super::super::tests::{RoomCount, insert, source};
use super::*;
#[test]
fn exhausted_count_revision_preserves_last_publication() -> Result<(), &'static str> {
let mut authority = source(2)?;
insert(&mut authority, 7, 1, 13)?;
let mut count = authority
.maintain_count(ViewId::new(core::num::NonZeroU64::MIN), RoomCount(1))
.map_err(|_| "count unavailable")?;
count.published.through.revision = u64::MAX;
let before = count.publication();
insert(&mut authority, 11, 1, 17)?;
assert_eq!(
count.refresh(&authority),
Err(ViewRefreshError::RevisionExhausted)
);
assert_eq!(count.publication(), before);
assert_eq!(count.membership.len(), 1);
Ok(())
}
}