prodash/
messages.rs

1use std::time::SystemTime;
2
3/// The severity of a message
4#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
5pub enum MessageLevel {
6    /// Rarely sent information related to the progress, not to be confused with the progress itself
7    Info,
8    /// Used to indicate that a task has failed, along with the reason
9    Failure,
10    /// Indicates a task was completed successfully
11    Success,
12}
13
14/// A message to be stored along with the progress tree.
15///
16/// It is created by [`Tree::message(…)`](./struct.Item.html#method.message).
17#[derive(Debug, Clone, Eq, PartialEq)]
18pub struct Message {
19    /// The time at which the message was sent.
20    pub time: SystemTime,
21    /// The severity of the message
22    pub level: MessageLevel,
23    /// The name of the task that created the `Message`
24    pub origin: String,
25    /// The message itself
26    pub message: String,
27}
28
29/// A ring buffer for messages.
30#[derive(Debug, Clone, Eq, PartialEq)]
31pub struct MessageRingBuffer {
32    pub(crate) buf: Vec<Message>,
33    cursor: usize,
34    total: usize,
35}
36
37impl MessageRingBuffer {
38    /// Create a new instance the ability to hold `capacity` amount of messages.
39    pub fn with_capacity(capacity: usize) -> MessageRingBuffer {
40        MessageRingBuffer {
41            buf: Vec::with_capacity(capacity),
42            cursor: 0,
43            total: 0,
44        }
45    }
46
47    /// Push a `message` from `origin` at severity `level` into the buffer, possibly overwriting the last message added.
48    pub fn push_overwrite(&mut self, level: MessageLevel, origin: String, message: impl Into<String>) {
49        let msg = Message {
50            time: SystemTime::now(),
51            level,
52            origin,
53            message: message.into(),
54        };
55        if self.has_capacity() {
56            self.buf.push(msg)
57        } else {
58            self.buf[self.cursor] = msg;
59            self.cursor = (self.cursor + 1) % self.buf.len();
60        }
61        self.total = self.total.wrapping_add(1);
62    }
63
64    /// Copy all messages currently contained in the buffer to `out`.
65    pub fn copy_all(&self, out: &mut Vec<Message>) {
66        out.clear();
67        if self.buf.is_empty() {
68            return;
69        }
70        out.extend_from_slice(&self.buf[self.cursor % self.buf.len()..]);
71        if self.cursor != self.buf.len() {
72            out.extend_from_slice(&self.buf[..self.cursor]);
73        }
74    }
75
76    /// Copy all new messages into `out` that where received since the last time this method was called provided
77    /// its `previous` return value.
78    pub fn copy_new(&self, out: &mut Vec<Message>, previous: Option<MessageCopyState>) -> MessageCopyState {
79        out.clear();
80        match previous {
81            Some(MessageCopyState { cursor, buf_len, total }) => {
82                if self.total.saturating_sub(total) >= self.buf.capacity() {
83                    self.copy_all(out);
84                } else {
85                    let new_elements_below_cap = self.buf.len().saturating_sub(buf_len);
86                    let cursor_ofs: isize = self.cursor as isize - cursor as isize;
87                    match cursor_ofs {
88                        // there was some capacity left without wrapping around
89                        0 => {
90                            out.extend_from_slice(&self.buf[self.buf.len() - new_elements_below_cap..]);
91                        }
92                        // cursor advanced
93                        c if c > 0 => {
94                            out.extend_from_slice(&self.buf[(cursor % self.buf.len())..self.cursor]);
95                        }
96                        // cursor wrapped around
97                        c if c < 0 => {
98                            out.extend_from_slice(&self.buf[(cursor % self.buf.len())..]);
99                            out.extend_from_slice(&self.buf[..self.cursor]);
100                        }
101                        _ => unreachable!("logic dictates that… yeah, you really shouldn't ever see this!"),
102                    }
103                }
104            }
105            None => self.copy_all(out),
106        };
107        MessageCopyState {
108            cursor: self.cursor,
109            buf_len: self.buf.len(),
110            total: self.total,
111        }
112    }
113
114    fn has_capacity(&self) -> bool {
115        self.buf.len() < self.buf.capacity()
116    }
117}
118
119/// State used to keep track of what's new since the last time message were copied.
120///
121/// Note that due to the nature of a ring buffer, there is no guarantee that you see all messages.
122pub struct MessageCopyState {
123    cursor: usize,
124    buf_len: usize,
125    total: usize,
126}