use super::ValidationError;
use crate::{ElicitCommunicator, ElicitResult, Elicitation, Prompt};
use elicitation_macros::instrumented_impl;
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DurationPositive(Duration);
#[cfg_attr(not(kani), instrumented_impl)]
impl DurationPositive {
pub fn new(duration: Duration) -> Result<Self, ValidationError> {
if duration.as_nanos() > 0 {
Ok(Self(duration))
} else {
Err(ValidationError::DurationNotPositive)
}
}
pub fn get(&self) -> Duration {
self.0
}
pub fn into_inner(self) -> Duration {
self.0
}
}
#[cfg_attr(not(kani), instrumented_impl)]
impl Prompt for DurationPositive {
fn prompt() -> Option<&'static str> {
Some("Please provide a positive duration (greater than zero seconds):")
}
}
#[cfg_attr(not(kani), instrumented_impl)]
impl Elicitation for DurationPositive {
type Style = <Duration as Elicitation>::Style;
#[tracing::instrument(skip(communicator))]
async fn elicit<C: ElicitCommunicator>(communicator: &C) -> ElicitResult<Self> {
tracing::debug!("Eliciting DurationPositive");
loop {
let duration = Duration::elicit(communicator).await?;
match Self::new(duration) {
Ok(valid) => {
tracing::debug!(duration = ?valid.0, "Valid positive duration");
return Ok(valid);
}
Err(e) => {
tracing::warn!(error = %e, "Duration not positive, re-prompting");
continue;
}
}
}
}
}
pub type DurationNonZero = DurationPositive;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_duration_positive_new_valid() {
let duration = Duration::from_secs(1);
let result = DurationPositive::new(duration);
assert!(result.is_ok());
}
#[test]
fn test_duration_positive_new_zero() {
let duration = Duration::from_secs(0);
let result = DurationPositive::new(duration);
assert!(result.is_err());
}
#[test]
fn test_duration_positive_get() {
let duration = Duration::from_millis(500);
let positive = DurationPositive::new(duration).unwrap();
assert_eq!(positive.get(), duration);
}
#[test]
fn test_duration_positive_into_inner() {
let duration = Duration::from_nanos(123456789);
let positive = DurationPositive::new(duration).unwrap();
assert_eq!(positive.into_inner(), duration);
}
#[test]
fn test_duration_non_zero_alias() {
let duration = Duration::from_secs(5);
let non_zero: DurationNonZero = DurationPositive::new(duration).unwrap();
assert_eq!(non_zero.into_inner(), duration);
}
}