use crate::JournalError;
use crate::Tag;
use std::collections::HashSet;
use std::fmt::Debug;
use std::hash::Hash;
use std::ops::Range;
#[cfg(feature = "serde")]
use serde::Deserialize;
#[cfg(feature = "serde")]
use serde::Serialize;
#[cfg(feature = "serde")]
use serde::de::Deserialize as DeserializeTrait;
#[cfg(feature = "serde")]
use serde::de::Deserializer;
#[cfg(feature = "serde")]
use serde::ser::Serialize as SerializeTrait;
#[cfg(feature = "serde")]
use serde::ser::Serializer;
#[derive(Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
struct Insert<K> {
pub key: K,
}
impl<K: Eq + Hash + Clone> Insert<K> {
pub fn forward(&self, set: &mut HashSet<K>) {
set.insert(self.key.clone());
}
pub fn reverse(&self, set: &mut HashSet<K>) {
set.remove(&self.key);
}
}
impl<K: Debug> std::fmt::Debug for Insert<K> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[+] {:?}", self.key)
}
}
#[derive(Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
struct Remove<K> {
pub key: K,
}
impl<K: Eq + Hash + Clone> Remove<K> {
pub fn forward(&self, set: &mut HashSet<K>) {
set.remove(&self.key);
}
pub fn reverse(&self, set: &mut HashSet<K>) {
set.insert(self.key.clone());
}
}
impl<K: Debug> std::fmt::Debug for Remove<K> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[-] {:?}", self.key)
}
}
#[derive(Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
enum JournalEntry<K, T> {
Insert(Insert<K>),
Remove(Remove<K>),
Tag(Tag<T>),
}
impl<K: Clone + Hash + Eq, T: Hash + Eq + Clone> JournalEntry<K, T> {
pub fn reverse(&self, set: &mut HashSet<K>) {
match self {
Self::Insert(entry_data) => entry_data.reverse(set),
Self::Remove(entry_data) => entry_data.reverse(set),
Self::Tag(_) => {}
}
}
pub fn forward(&self, set: &mut HashSet<K>) {
match self {
Self::Insert(entry_data) => entry_data.forward(set),
Self::Remove(entry_data) => entry_data.forward(set),
Self::Tag(_) => {}
}
}
}
impl<K: Debug + Hash + Eq, T: Debug + Hash + Eq> std::fmt::Debug for JournalEntry<K, T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
JournalEntry::Insert(entry) => {
write!(f, "{:?}", entry)
}
JournalEntry::Remove(entry) => {
write!(f, "{:?}", entry)
}
JournalEntry::Tag(entry) => {
write!(f, "{:?}", entry)
}
}
}
}
pub struct JournalSet<K: Eq + Hash, T: Eq + Hash> {
journal: Vec<JournalEntry<K, T>>,
tags: HashSet<T>,
set: HashSet<K>,
current_tag_index: Option<usize>,
}
impl<K: Hash + Eq, T: Hash + Eq> Default for JournalSet<K, T> {
fn default() -> Self {
Self::new()
}
}
impl<K: Hash + Eq, T: Hash + Eq> JournalSet<K, T> {
pub fn new() -> Self {
Self {
journal: Vec::new(),
tags: HashSet::new(),
set: HashSet::new(),
current_tag_index: None,
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
journal: Vec::with_capacity(capacity),
tags: HashSet::with_capacity((capacity / 2).min(4)),
set: HashSet::with_capacity(capacity),
current_tag_index: None,
}
}
#[inline]
pub fn len(&self) -> usize {
self.set.len()
}
#[inline]
pub fn journal_len(&self) -> usize {
self.journal.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.set.is_empty()
}
pub fn current_tag(&self) -> Option<&T> {
let Some(i_tag) = self.current_tag_index else {
return None;
};
let JournalEntry::Tag(tag) = &self.journal[i_tag] else {
panic!("Tag not at tag index");
};
Some(&tag.tag)
}
#[inline]
pub fn as_hashset(&self) -> &HashSet<K> {
&self.set
}
pub fn clear_all(&mut self) {
self.set.clear();
self.journal.clear();
self.tags.clear();
self.current_tag_index = None;
}
fn transaction_prep(&mut self) {
let Some(i_tag) = self.current_tag_index.take() else {
return;
};
let i_trunc = i_tag + 1;
for entry in self.journal[i_trunc..].iter() {
if let JournalEntry::Tag(tag) = entry {
self.tags.remove(&tag.tag);
}
}
self.journal.truncate(i_trunc);
}
fn i_tag_reverse_seek<MatchFn: Fn(&T) -> bool>(
&mut self,
match_fn: MatchFn,
) -> Option<Range<usize>> {
let i_reverse_end = self.current_tag_index.unwrap_or(self.journal.len());
let Some(i_tag_rev_rel) = self.journal[..i_reverse_end]
.iter()
.rev()
.position(|entry| {
if let JournalEntry::Tag(tag) = entry
&& match_fn(&tag.tag)
{
true
} else {
false
}
})
else {
return None;
};
let i_tag = i_reverse_end - i_tag_rev_rel - 1;
self.current_tag_index = Some(i_tag);
let i_reverse_start = i_tag + 1;
Some(i_reverse_start..i_reverse_end)
}
fn i_tag_forward_seek<MatchFn: Fn(&T) -> bool>(
&mut self,
match_fn: MatchFn,
) -> Option<Range<usize>> {
let Some(i_current_tag) = self.current_tag_index else {
return None;
};
let i_forward_start = i_current_tag + 1;
let Some(i_next_tag_rel) = self.journal[i_forward_start..].iter().position(|entry| {
if let JournalEntry::Tag(tag) = entry
&& match_fn(&tag.tag)
{
true
} else {
false
}
}) else {
return None;
};
let i_next_tag = i_forward_start + i_next_tag_rel;
self.current_tag_index = Some(i_next_tag);
let i_forward_end = i_next_tag;
Some(i_forward_start..i_forward_end)
}
pub fn retain_tags<RetainFn: Fn(&T) -> bool>(&mut self, retain_fn: RetainFn) {
self.current_tag_index = None;
self.journal.retain(|entry| {
let JournalEntry::Tag(tag) = entry else {
return true;
};
let retain = retain_fn(&tag.tag);
if !retain {
self.tags.remove(&tag.tag);
}
retain
});
}
}
impl<K: Eq + Hash + Clone, T: Hash + Eq + Clone> JournalSet<K, T> {
pub fn from_iter<Iter: Iterator<Item = K>>(iter: Iter, init_tag: Option<T>) -> Self {
let set: HashSet<K> = HashSet::from_iter(iter);
let capacity = set.capacity() + init_tag.is_some() as usize;
let mut journal_set = Self {
journal: Vec::with_capacity(capacity),
tags: HashSet::with_capacity((capacity / 2).min(4)),
set,
current_tag_index: None,
};
if let Some(tag) = init_tag {
journal_set.tags.insert(tag.clone());
journal_set.journal.push(JournalEntry::Tag(Tag { tag }));
}
for key in journal_set.set.iter() {
journal_set
.journal
.push(JournalEntry::Insert(Insert { key: key.clone() }));
}
journal_set
}
pub fn insert(&mut self, key: K) -> bool {
self.transaction_prep();
let entry = JournalEntry::Insert(Insert { key: key.clone() });
self.journal.push(entry);
self.set.insert(key)
}
pub fn contains(&self, key: &K) -> bool {
self.set.contains(key)
}
pub fn remove(&mut self, key: K) -> bool {
self.transaction_prep();
let removed = self.set.remove(&key);
if removed {
self.journal.push(JournalEntry::Remove(Remove { key }));
}
removed
}
pub fn clear(&mut self) {
self.transaction_prep();
for key in self.set.iter() {
self.journal
.push(JournalEntry::Remove(Remove { key: key.clone() }));
}
self.set.clear();
}
pub fn append_tag(&mut self, tag: T) -> Result<(), JournalError<T>> {
if self.tags.contains(&tag) {
return Err(JournalError::TagExists { tag });
}
self.current_tag_index = Some(self.journal.len());
self.journal
.push(JournalEntry::Tag(Tag { tag: tag.clone() }));
self.tags.insert(tag);
Ok(())
}
pub fn reverse_to_next(&mut self) -> bool {
let Some(range) = self.i_tag_reverse_seek(|_| true) else {
return false;
};
if range.is_empty() {
return false;
}
self.reverse_range(range);
true
}
pub fn reverse_to_tag(&mut self, tag: &T) -> bool {
let Some(range) = self.i_tag_reverse_seek(|other_tag| other_tag == tag) else {
return false;
};
if range.is_empty() {
return false;
}
self.reverse_range(range);
true
}
pub fn forward_to_next(&mut self) -> bool {
let Some(range) = self.i_tag_forward_seek(|_| true) else {
return false;
};
if range.is_empty() {
return false;
}
self.forward_range(range);
true
}
pub fn forward_to_tag(&mut self, tag: &T) -> bool {
let Some(range) = self.i_tag_forward_seek(|other_tag| other_tag == tag) else {
return false;
};
if range.is_empty() {
return false;
}
self.forward_range(range);
true
}
fn reverse_range(&mut self, range: Range<usize>) {
for entry in self.journal[range].iter().rev() {
entry.reverse(&mut self.set);
}
}
fn forward_range(&mut self, range: Range<usize>) {
for entry in self.journal[range].iter() {
entry.forward(&mut self.set);
}
}
pub fn generate_journal(&self, truncate_future: bool) -> JournalSetJournal<K, T> {
let i_copy_start = 0;
let i_copy_end = if truncate_future && let Some(i_tag) = self.current_tag_index {
i_tag + 1
} else {
self.journal.len()
};
let journal_src = &self.journal[i_copy_start..i_copy_end];
let journal = journal_src.to_vec();
JournalSetJournal { journal }
}
pub fn from_journal(val: &JournalSetJournal<K, T>) -> Self {
let capacity = val.journal.len();
let set = HashSet::with_capacity(capacity);
let tags = HashSet::with_capacity(capacity);
let mut journal_map = Self {
set,
tags,
journal: val.journal.clone(),
current_tag_index: None,
};
for entry in journal_map.journal.iter() {
entry.forward(&mut journal_map.set);
if let JournalEntry::Tag(tag) = entry {
journal_map.tags.insert(tag.tag.clone());
}
}
journal_map
}
}
impl<K: Debug + Eq + Hash, T: Debug + Eq + Hash> std::fmt::Debug for JournalSet<K, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let alternate = f.alternate();
write!(f, "{{")?;
for (i_entry, entry) in self.journal.iter().enumerate() {
if alternate {
write!(f, "\n\t")?;
}
if let Some(i_tag) = self.current_tag_index
&& i_tag == i_entry
{
write!(f, "@ ")?;
}
write!(f, "{:?},", entry)?;
}
if alternate {
writeln!(f)?;
}
write!(f, "}}")?;
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct JournalSetJournal<K: Hash + Eq, T: Hash + Eq> {
journal: Vec<JournalEntry<K, T>>,
}
impl<K: Hash + Eq, T: Hash + Eq> JournalSetJournal<K, T> {
#[inline]
pub fn len(&self) -> usize {
self.journal.len()
}
}
#[cfg(feature = "serde")]
impl<K: SerializeTrait + Hash + Eq, T: SerializeTrait + Eq + Hash> SerializeTrait
for JournalSetJournal<K, T>
{
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.journal.serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, K: DeserializeTrait<'de> + Hash + Eq, T: DeserializeTrait<'de> + Eq + Hash>
DeserializeTrait<'de> for JournalSetJournal<K, T>
{
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
Ok(Self {
journal: Vec::deserialize(deserializer)?,
})
}
}