sbom_tools/tui/viewmodel/
status.rs1use std::time::{Duration, Instant};
7
8#[derive(Debug, Clone, Default)]
27pub struct StatusMessage {
28 message: Option<String>,
30 set_at: Option<Instant>,
32 auto_clear_after: Option<Duration>,
34}
35
36impl StatusMessage {
37 #[must_use]
39 pub fn new() -> Self {
40 Self::default()
41 }
42
43 #[must_use]
45 pub const fn with_auto_clear(duration: Duration) -> Self {
46 Self {
47 message: None,
48 set_at: None,
49 auto_clear_after: Some(duration),
50 }
51 }
52
53 pub fn set(&mut self, msg: impl Into<String>) {
55 self.message = Some(msg.into());
56 self.set_at = Some(Instant::now());
57 }
58
59 pub fn clear(&mut self) {
61 self.message = None;
62 self.set_at = None;
63 }
64
65 pub fn message(&mut self) -> Option<&str> {
67 if let (Some(set_at), Some(duration)) = (self.set_at, self.auto_clear_after)
69 && set_at.elapsed() >= duration {
70 self.message = None;
71 self.set_at = None;
72 }
73 self.message.as_deref()
74 }
75
76 #[must_use]
80 pub fn peek(&self) -> Option<&str> {
81 self.message.as_deref()
82 }
83
84 #[must_use]
86 pub const fn has_message(&self) -> bool {
87 self.message.is_some()
88 }
89
90 #[must_use]
94 pub const fn as_option(&self) -> &Option<String> {
95 &self.message
96 }
97
98 pub const fn take(&mut self) -> Option<String> {
100 self.set_at = None;
101 self.message.take()
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use std::thread;
109
110 #[test]
111 fn test_status_message_set_clear() {
112 let mut status = StatusMessage::new();
113
114 assert!(!status.has_message());
115 assert!(status.peek().is_none());
116
117 status.set("Test message");
118 assert!(status.has_message());
119 assert_eq!(status.peek(), Some("Test message"));
120
121 status.clear();
122 assert!(!status.has_message());
123 assert!(status.peek().is_none());
124 }
125
126 #[test]
127 fn test_status_message_take() {
128 let mut status = StatusMessage::new();
129
130 status.set("Take me");
131 let taken = status.take();
132
133 assert_eq!(taken, Some("Take me".to_string()));
134 assert!(!status.has_message());
135 }
136
137 #[test]
138 fn test_status_message_auto_clear() {
139 let mut status = StatusMessage::with_auto_clear(Duration::from_millis(50));
140
141 status.set("Auto clear message");
142 assert!(status.message().is_some());
143
144 thread::sleep(Duration::from_millis(60));
146 assert!(status.message().is_none());
147 }
148
149 #[test]
150 fn test_status_message_no_auto_clear_default() {
151 let mut status = StatusMessage::new();
152
153 status.set("No auto clear");
154 thread::sleep(Duration::from_millis(10));
155 assert!(status.message().is_some()); }
157}