use std::collections::VecDeque;
use std::sync::Arc;
use std::time::Duration;
use serde::{Deserialize, Serialize};
use serde_json::Value;
pub const DEFAULT_CAP: usize = 10_000;
pub const DEFAULT_DROP: usize = 1_000;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct JournalEntry {
pub seq: u64,
pub id: String,
pub params: Value,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CommandRun {
pub id: &'static str,
pub ok: bool,
pub took: Duration,
}
pub type RunObserver = Arc<dyn Fn(CommandRun) + Send + Sync>;
#[derive(Debug, Clone)]
pub struct Journal {
entries: VecDeque<JournalEntry>,
cap: usize,
drop: usize,
nested: bool,
depth: usize,
next_seq: u64,
observer: Observer,
}
#[derive(Clone, Default)]
struct Observer(Option<RunObserver>);
impl std::fmt::Debug for Observer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(if self.0.is_some() { "Observer(set)" } else { "Observer(none)" })
}
}
impl Default for Journal {
fn default() -> Self {
Self::new()
}
}
impl Journal {
pub fn new() -> Self {
Self::with_cap(DEFAULT_CAP, DEFAULT_DROP)
}
pub fn with_cap(cap: usize, drop: usize) -> Self {
let cap = cap.max(1);
Self { entries: VecDeque::new(), cap, drop: drop.clamp(1, cap), nested: false, depth: 0, next_seq: 0, observer: Observer::default() }
}
pub fn record_nested(mut self, yes: bool) -> Self {
self.nested = yes;
self
}
pub fn set_observer(&mut self, observer: Option<RunObserver>) {
self.observer = Observer(observer);
}
pub fn observer(&self) -> Option<&RunObserver> {
self.observer.0.as_ref()
}
pub fn depth(&self) -> usize {
self.depth
}
pub fn set_depth(&mut self, depth: usize) {
self.depth = depth;
}
pub fn records(&self, depth: usize) -> bool {
depth == 0 || self.nested
}
pub fn push(&mut self, id: impl Into<String>, params: Value) {
self.entries.push_back(JournalEntry { seq: self.next_seq, id: id.into(), params });
self.next_seq = self.next_seq.saturating_add(1);
if self.entries.len() > self.cap {
let n = self.drop.min(self.entries.len());
self.entries.drain(..n);
}
}
pub fn entries(&self) -> impl DoubleEndedIterator<Item = &JournalEntry> + ExactSizeIterator {
self.entries.iter()
}
pub fn since(&self, seq: u64) -> impl Iterator<Item = &JournalEntry> {
self.entries.iter().filter(move |e| e.seq >= seq)
}
pub fn last(&self) -> Option<&JournalEntry> {
self.entries.back()
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn next_seq(&self) -> u64 {
self.next_seq
}
pub fn truncate(&mut self, len: usize) {
self.entries.truncate(len);
}
pub fn retain(&mut self, mut keep: impl FnMut(&JournalEntry) -> bool) {
self.entries.retain(|e| keep(e));
}
pub fn pop(&mut self) -> Option<JournalEntry> {
self.entries.pop_back()
}
pub fn clear(&mut self) {
self.entries.clear();
}
pub fn to_json(&self) -> Value {
Value::Array(self.entries.iter().filter_map(|e| serde_json::to_value(e).ok()).collect())
}
}