use std::convert::Infallible;
use serde::{Deserialize, Serialize};
use super::MarkAsRead;
use crate::{
actions::filters::{Filter, FilterableEntries},
entry::EntryId,
};
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Newer {
pub last_read_id: Option<EntryId>,
}
impl Newer {
#[must_use]
pub const fn new() -> Self {
Self { last_read_id: None }
}
#[must_use]
pub const fn last_read(&self) -> Option<&EntryId> {
self.last_read_id.as_ref()
}
}
impl MarkAsRead for Newer {
type Err = Infallible;
async fn mark_as_read(&mut self, id: &EntryId) -> Result<(), Self::Err> {
self.last_read_id = Some(id.clone());
Ok(())
}
async fn set_read_only(&mut self) {
}
}
impl Filter for Newer {
type Err = Infallible;
#[tracing::instrument(level = "debug", name = "filter_read", skip_all)]
async fn filter(&mut self, mut entries: FilterableEntries<'_>) -> Result<(), Self::Err> {
if let Some(last_read_id) = &self.last_read_id {
if let Some(last_read_id_pos) = entries.iter().position(|x| {
let Some(id) = &x.id else { return false };
last_read_id == id
}) {
let removed_elems = entries.drain(last_read_id_pos..).count();
tracing::debug!("Removed {removed_elems} already read entries");
tracing::trace!("Unread entries remaining: {entries:#?}");
}
}
Ok(())
}
}
impl Default for Newer {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use crate::entry::Entry;
use super::*;
fn entry_id(id: &str) -> EntryId {
EntryId::new(id.to_owned()).unwrap()
}
#[tokio::test]
async fn mark_as_read() {
let mut rf = Newer::new();
rf.mark_as_read(&entry_id("13")).await.unwrap();
assert_eq!(rf.last_read_id.as_deref().unwrap(), "13");
rf.mark_as_read(&entry_id("1002")).await.unwrap();
assert_eq!(rf.last_read_id.as_deref().unwrap(), "1002");
}
#[tokio::test]
async fn last_read() {
let mut rf = Newer::new();
assert_eq!(None, rf.last_read());
rf.mark_as_read(&entry_id("0")).await.unwrap();
rf.mark_as_read(&entry_id("1")).await.unwrap();
rf.mark_as_read(&entry_id("2")).await.unwrap();
assert_eq!(Some(&entry_id("2")), rf.last_read());
rf.mark_as_read(&entry_id("4")).await.unwrap();
assert_eq!(Some(&entry_id("4")), rf.last_read());
rf.mark_as_read(&entry_id("100")).await.unwrap();
rf.mark_as_read(&entry_id("101")).await.unwrap();
rf.mark_as_read(&entry_id("200")).await.unwrap();
assert_eq!(Some(&entry_id("200")), rf.last_read());
}
#[tokio::test]
async fn remove_read_long_list() {
let mut rf = Newer::new();
rf.mark_as_read(&entry_id("3")).await.unwrap();
let mut entries = vec![
Entry::default(),
Entry::builder().id_raw(entry_id("5")).build(),
Entry::builder().id_raw(entry_id("4")).build(),
Entry::default(),
Entry::builder().id_raw(entry_id("0")).build(),
Entry::builder().id_raw(entry_id("1")).build(),
Entry::builder().id_raw(entry_id("3")).build(),
Entry::default(),
Entry::builder().id_raw(entry_id("6")).build(),
Entry::builder().id_raw(entry_id("8")).build(),
];
rf.filter(FilterableEntries::new(&mut entries))
.await
.unwrap();
let entries = entries.iter().map(|e| e.id.as_deref()).collect::<Vec<_>>();
assert_eq!(
&entries,
&[None, Some("5"), Some("4"), None, Some("0"), Some("1")]
);
}
#[tokio::test]
async fn remove_read_single_different() {
let mut rf = Newer::new();
rf.mark_as_read(&entry_id("3")).await.unwrap();
let mut entries = vec![Entry::builder().id_raw(entry_id("1")).build()];
rf.filter(FilterableEntries::new(&mut entries))
.await
.unwrap();
let entries_ids = entries.iter().map(|e| e.id.as_deref()).collect::<Vec<_>>();
assert_eq!(&entries_ids, &[Some("1")]);
}
#[tokio::test]
async fn remove_read_single_same() {
let mut rf = Newer::new();
rf.mark_as_read(&entry_id("1")).await.unwrap();
let mut entries = vec![Entry::builder().id_raw(entry_id("1")).build()];
rf.filter(FilterableEntries::new(&mut entries))
.await
.unwrap();
let entries_ids = entries.iter().map(|e| e.id.as_deref()).collect::<Vec<_>>();
assert_eq!(&entries_ids, &[]);
}
}