use crate::constants::MESSAGE_DEFAULT_DURATION;
use std::time::{Duration, Instant};
pub const DEFAULT_MESSAGE_DURATION: Duration = MESSAGE_DEFAULT_DURATION;
#[derive(Clone, Debug)]
pub struct MessageState {
message: Option<String>,
message_time: Option<Instant>,
duration: Duration,
}
impl Default for MessageState {
fn default() -> Self {
Self::new()
}
}
impl MessageState {
pub fn new() -> Self {
Self {
message: None,
message_time: None,
duration: DEFAULT_MESSAGE_DURATION,
}
}
pub fn with_duration(duration: Duration) -> Self {
Self {
message: None,
message_time: None,
duration,
}
}
pub fn set(&mut self, message: String) {
self.message = Some(message);
self.message_time = Some(Instant::now());
}
pub fn set_with_duration(&mut self, message: String, duration: Duration) {
self.message = Some(message);
self.message_time = Some(Instant::now());
self.duration = duration;
}
pub fn get(&self) -> Option<&str> {
self.message.as_deref()
}
pub fn has_message(&self) -> bool {
self.message.is_some()
}
pub fn clear(&mut self) {
self.message = None;
self.message_time = None;
}
pub fn check_timeout(&mut self) -> bool {
if let Some(time) = self.message_time {
if time.elapsed() >= self.duration {
self.clear();
return true; }
}
false
}
pub fn remaining(&self) -> Option<Duration> {
self.message_time.map(|time| {
let elapsed = time.elapsed();
self.duration.saturating_sub(elapsed)
})
}
pub fn is_expiring(&self) -> bool {
self.remaining()
.map(|r| r < Duration::from_secs(1))
.unwrap_or(false)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
#[test]
fn test_set_and_get() {
let mut msg = MessageState::new();
assert_eq!(msg.get(), None);
msg.set("Hello".to_string());
assert_eq!(msg.get(), Some("Hello"));
}
#[test]
fn test_clear() {
let mut msg = MessageState::new();
msg.set("Test".to_string());
assert!(msg.has_message());
msg.clear();
assert!(!msg.has_message());
assert_eq!(msg.get(), None);
}
#[test]
fn test_timeout() {
let mut msg = MessageState::with_duration(Duration::from_millis(100));
msg.set("Test".to_string());
assert!(!msg.check_timeout());
assert!(msg.has_message());
thread::sleep(Duration::from_millis(150));
assert!(msg.check_timeout());
assert!(!msg.has_message());
}
#[test]
fn test_remaining() {
let mut msg = MessageState::with_duration(Duration::from_secs(5));
msg.set("Test".to_string());
let remaining = msg.remaining().unwrap();
assert!(remaining <= Duration::from_secs(5));
assert!(remaining > Duration::from_secs(4));
}
#[test]
fn test_is_expiring() {
let mut msg = MessageState::with_duration(Duration::from_millis(2000));
msg.set("Test".to_string());
assert!(!msg.is_expiring());
thread::sleep(Duration::from_millis(1200));
assert!(msg.is_expiring());
}
#[test]
fn test_message_state_new() {
let msg = MessageState::new();
assert!(!msg.has_message());
assert_eq!(msg.get(), None);
assert_eq!(msg.remaining(), None);
assert!(!msg.is_expiring());
}
#[test]
fn test_message_state_default() {
let msg = MessageState::default();
assert!(!msg.has_message());
}
#[test]
fn test_message_state_with_duration() {
let msg = MessageState::with_duration(Duration::from_secs(10));
assert!(!msg.has_message());
}
#[test]
fn test_set_overwrites() {
let mut msg = MessageState::new();
msg.set("First".to_string());
assert_eq!(msg.get(), Some("First"));
msg.set("Second".to_string());
assert_eq!(msg.get(), Some("Second"));
}
#[test]
fn test_set_with_duration() {
let mut msg = MessageState::new();
msg.set_with_duration("Test".to_string(), Duration::from_millis(50));
assert_eq!(msg.get(), Some("Test"));
thread::sleep(Duration::from_millis(100));
assert!(msg.check_timeout());
}
#[test]
fn test_has_message() {
let msg = MessageState::new();
assert!(!msg.has_message());
let mut msg = MessageState::new();
msg.set("Test".to_string());
assert!(msg.has_message());
}
#[test]
fn test_check_timeout_returns_true() {
let mut msg = MessageState::with_duration(Duration::from_millis(50));
msg.set("Test".to_string());
thread::sleep(Duration::from_millis(100));
assert!(msg.check_timeout());
}
#[test]
fn test_check_timeout_no_message() {
let mut msg = MessageState::new();
assert!(!msg.check_timeout());
}
#[test]
fn test_remaining_no_message() {
let msg = MessageState::new();
assert_eq!(msg.remaining(), None);
}
#[test]
fn test_is_expiring_no_message() {
let msg = MessageState::new();
assert!(!msg.is_expiring());
}
#[test]
fn test_is_expiring_already_expired() {
let mut msg = MessageState::with_duration(Duration::from_millis(50));
msg.set("Test".to_string());
thread::sleep(Duration::from_millis(100));
msg.check_timeout();
assert!(!msg.is_expiring());
}
#[test]
fn test_default_message_duration() {
assert_eq!(DEFAULT_MESSAGE_DURATION.as_secs(), 3);
}
#[test]
fn test_set_empty_string() {
let mut msg = MessageState::new();
msg.set("".to_string());
assert_eq!(msg.get(), Some(""));
}
#[test]
fn test_multiple_timeouts() {
let mut msg = MessageState::with_duration(Duration::from_millis(100));
msg.set("First".to_string());
thread::sleep(Duration::from_millis(150));
assert!(msg.check_timeout());
assert!(!msg.has_message());
msg.set("Second".to_string());
thread::sleep(Duration::from_millis(150));
assert!(msg.check_timeout());
assert!(!msg.has_message());
}
#[test]
fn test_check_timeout_returns_true_only_once() {
let mut msg = MessageState::with_duration(Duration::from_millis(50));
msg.set("Test".to_string());
thread::sleep(Duration::from_millis(100));
assert!(msg.check_timeout());
assert!(!msg.check_timeout());
}
}