ashlar_command/
journal.rs1use std::collections::VecDeque;
4use std::sync::Arc;
5use std::time::Duration;
6
7use serde::{Deserialize, Serialize};
8use serde_json::Value;
9
10pub const DEFAULT_CAP: usize = 10_000;
12pub const DEFAULT_DROP: usize = 1_000;
14
15#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
17pub struct JournalEntry {
18 pub seq: u64,
20 pub id: String,
21 pub params: Value,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq)]
27pub struct CommandRun {
28 pub id: &'static str,
29 pub ok: bool,
30 pub took: Duration,
31}
32
33pub type RunObserver = Arc<dyn Fn(CommandRun) + Send + Sync>;
36
37#[derive(Debug, Clone)]
44pub struct Journal {
45 entries: VecDeque<JournalEntry>,
46 cap: usize,
47 drop: usize,
48 nested: bool,
49 depth: usize,
50 next_seq: u64,
51 observer: Observer,
52}
53
54#[derive(Clone, Default)]
56struct Observer(Option<RunObserver>);
57
58impl std::fmt::Debug for Observer {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 f.write_str(if self.0.is_some() { "Observer(set)" } else { "Observer(none)" })
61 }
62}
63
64impl Default for Journal {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70impl Journal {
71 pub fn new() -> Self {
72 Self::with_cap(DEFAULT_CAP, DEFAULT_DROP)
73 }
74
75 pub fn with_cap(cap: usize, drop: usize) -> Self {
77 let cap = cap.max(1);
78 Self { entries: VecDeque::new(), cap, drop: drop.clamp(1, cap), nested: false, depth: 0, next_seq: 0, observer: Observer::default() }
79 }
80
81 pub fn record_nested(mut self, yes: bool) -> Self {
83 self.nested = yes;
84 self
85 }
86
87 pub fn set_observer(&mut self, observer: Option<RunObserver>) {
90 self.observer = Observer(observer);
91 }
92
93 pub fn observer(&self) -> Option<&RunObserver> {
94 self.observer.0.as_ref()
95 }
96
97 pub fn depth(&self) -> usize {
99 self.depth
100 }
101
102 pub fn set_depth(&mut self, depth: usize) {
106 self.depth = depth;
107 }
108
109 pub fn records(&self, depth: usize) -> bool {
111 depth == 0 || self.nested
112 }
113
114 pub fn push(&mut self, id: impl Into<String>, params: Value) {
116 self.entries.push_back(JournalEntry { seq: self.next_seq, id: id.into(), params });
117 self.next_seq = self.next_seq.saturating_add(1);
118 if self.entries.len() > self.cap {
119 let n = self.drop.min(self.entries.len());
120 self.entries.drain(..n);
121 }
122 }
123
124 pub fn entries(&self) -> impl DoubleEndedIterator<Item = &JournalEntry> + ExactSizeIterator {
125 self.entries.iter()
126 }
127
128 pub fn since(&self, seq: u64) -> impl Iterator<Item = &JournalEntry> {
130 self.entries.iter().filter(move |e| e.seq >= seq)
131 }
132
133 pub fn last(&self) -> Option<&JournalEntry> {
134 self.entries.back()
135 }
136
137 pub fn len(&self) -> usize {
138 self.entries.len()
139 }
140
141 pub fn is_empty(&self) -> bool {
142 self.entries.is_empty()
143 }
144
145 pub fn next_seq(&self) -> u64 {
147 self.next_seq
148 }
149
150 pub fn truncate(&mut self, len: usize) {
153 self.entries.truncate(len);
154 }
155
156 pub fn retain(&mut self, mut keep: impl FnMut(&JournalEntry) -> bool) {
158 self.entries.retain(|e| keep(e));
159 }
160
161 pub fn pop(&mut self) -> Option<JournalEntry> {
163 self.entries.pop_back()
164 }
165
166 pub fn clear(&mut self) {
167 self.entries.clear();
168 }
169
170 pub fn to_json(&self) -> Value {
172 Value::Array(self.entries.iter().filter_map(|e| serde_json::to_value(e).ok()).collect())
173 }
174}