Coverage Report

Created: 2026-08-05 07:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/prodash-31.0.0/src/messages.rs
Line
Count
Source
1
use std::time::SystemTime;
2
3
/// The severity of a message
4
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
5
pub 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)]
18
pub 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)]
31
pub struct MessageRingBuffer {
32
    pub(crate) buf: Vec<Message>,
33
    cursor: usize,
34
    total: usize,
35
}
36
37
impl MessageRingBuffer {
38
    /// Create a new instance the ability to hold `capacity` amount of messages.
39
0
    pub fn with_capacity(capacity: usize) -> MessageRingBuffer {
40
0
        MessageRingBuffer {
41
0
            buf: Vec::with_capacity(capacity),
42
0
            cursor: 0,
43
0
            total: 0,
44
0
        }
45
0
    }
46
47
    /// Push a `message` from `origin` at severity `level` into the buffer, possibly overwriting the last message added.
48
0
    pub fn push_overwrite(&mut self, level: MessageLevel, origin: String, message: impl Into<String>) {
49
0
        let msg = Message {
50
0
            time: SystemTime::now(),
51
0
            level,
52
0
            origin,
53
0
            message: message.into(),
54
0
        };
55
0
        if self.has_capacity() {
56
0
            self.buf.push(msg)
57
0
        } else {
58
0
            self.buf[self.cursor] = msg;
59
0
            self.cursor = (self.cursor + 1) % self.buf.len();
60
0
        }
61
0
        self.total = self.total.wrapping_add(1);
62
0
    }
63
64
    /// Copy all messages currently contained in the buffer to `out`.
65
0
    pub fn copy_all(&self, out: &mut Vec<Message>) {
66
0
        out.clear();
67
0
        if self.buf.is_empty() {
68
0
            return;
69
0
        }
70
0
        out.extend_from_slice(&self.buf[self.cursor % self.buf.len()..]);
71
0
        if self.cursor != self.buf.len() {
72
0
            out.extend_from_slice(&self.buf[..self.cursor]);
73
0
        }
74
0
    }
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
0
    pub fn copy_new(&self, out: &mut Vec<Message>, previous: Option<MessageCopyState>) -> MessageCopyState {
79
0
        out.clear();
80
0
        match previous {
81
0
            Some(MessageCopyState { cursor, buf_len, total }) => {
82
0
                if self.total.saturating_sub(total) >= self.buf.capacity() {
83
0
                    self.copy_all(out);
84
0
                } else {
85
0
                    let new_elements_below_cap = self.buf.len().saturating_sub(buf_len);
86
0
                    let cursor_ofs: isize = self.cursor as isize - cursor as isize;
87
0
                    match cursor_ofs {
88
                        // there was some capacity left without wrapping around
89
0
                        0 => {
90
0
                            out.extend_from_slice(&self.buf[self.buf.len() - new_elements_below_cap..]);
91
0
                        }
92
                        // cursor advanced
93
0
                        c if c > 0 => {
94
0
                            out.extend_from_slice(&self.buf[(cursor % self.buf.len())..self.cursor]);
95
0
                        }
96
                        // cursor wrapped around
97
0
                        c if c < 0 => {
98
0
                            out.extend_from_slice(&self.buf[(cursor % self.buf.len())..]);
99
0
                            out.extend_from_slice(&self.buf[..self.cursor]);
100
0
                        }
101
0
                        _ => unreachable!("logic dictates that… yeah, you really shouldn't ever see this!"),
102
                    }
103
                }
104
            }
105
0
            None => self.copy_all(out),
106
        };
107
0
        MessageCopyState {
108
0
            cursor: self.cursor,
109
0
            buf_len: self.buf.len(),
110
0
            total: self.total,
111
0
        }
112
0
    }
113
114
0
    fn has_capacity(&self) -> bool {
115
0
        self.buf.len() < self.buf.capacity()
116
0
    }
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.
122
pub struct MessageCopyState {
123
    cursor: usize,
124
    buf_len: usize,
125
    total: usize,
126
}