use std::time::{Duration, Instant};
use std::collections::VecDeque;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NotificationLevel {
Info,
Success,
Warning,
Error,
}
#[derive(Debug, Clone)]
pub struct Notification {
pub id: u64,
pub message: String,
pub level: NotificationLevel,
pub created_at: Instant,
pub duration: Option<Duration>,
}
impl Notification {
pub fn is_expired(&self) -> bool {
if let Some(duration) = self.duration {
self.created_at.elapsed() >= duration
} else {
false
}
}
}
pub struct NotificationManager {
notifications: VecDeque<Notification>,
max_visible: usize,
next_id: u64,
}
impl NotificationManager {
pub fn new(max_visible: usize) -> Self {
Self {
notifications: VecDeque::new(),
max_visible,
next_id: 0,
}
}
pub fn notify(
&mut self,
message: impl Into<String>,
level: NotificationLevel,
duration: Option<Duration>,
) -> u64 {
let id = self.next_id;
self.next_id += 1;
let notification = Notification {
id,
message: message.into(),
level,
created_at: Instant::now(),
duration,
};
while self.notifications.len() >= self.max_visible {
self.notifications.pop_front();
}
self.notifications.push_back(notification);
id
}
pub fn info(&mut self, message: impl Into<String>) -> u64 {
self.notify(message, NotificationLevel::Info, Some(Duration::from_secs(3)))
}
pub fn success(&mut self, message: impl Into<String>) -> u64 {
self.notify(message, NotificationLevel::Success, Some(Duration::from_secs(3)))
}
pub fn warning(&mut self, message: impl Into<String>) -> u64 {
self.notify(message, NotificationLevel::Warning, Some(Duration::from_secs(5)))
}
pub fn error(&mut self, message: impl Into<String>) -> u64 {
self.notify(message, NotificationLevel::Error, None)
}
pub fn dismiss(&mut self, id: u64) {
self.notifications.retain(|n| n.id != id);
}
pub fn dismiss_all(&mut self) {
self.notifications.clear();
}
pub fn cleanup_expired(&mut self) {
self.notifications.retain(|n| !n.is_expired());
}
pub fn visible(&self) -> impl Iterator<Item = &Notification> {
self.notifications.iter()
}
pub fn count(&self) -> usize {
self.notifications.len()
}
}
impl Default for NotificationManager {
fn default() -> Self {
Self::new(3) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_notification_lifecycle() {
let mut nm = NotificationManager::new(5);
let id = nm.info("Test message");
assert_eq!(nm.count(), 1);
nm.dismiss(id);
assert_eq!(nm.count(), 0);
}
#[test]
fn test_notification_levels() {
let mut nm = NotificationManager::new(5);
nm.info("info");
nm.success("success");
nm.warning("warning");
nm.error("error");
assert_eq!(nm.count(), 4);
}
}