use std::time::Duration;
use serde::Deserialize;
use crate::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TaskMonitorOptions {
timeout: Option<Duration>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct TaskMonitorReport {
pub timeout_seconds: Option<u64>,
pub cancelled: bool,
pub message: String,
pub progress: i64,
pub maximum: i64,
}
impl TaskMonitorOptions {
pub const fn none() -> Self {
Self { timeout: None }
}
pub fn timeout(timeout: Duration) -> Result<Self> {
validate_timeout(timeout)?;
Ok(Self {
timeout: Some(timeout),
})
}
pub fn timeout_duration(&self) -> Option<Duration> {
self.timeout
}
pub(crate) fn timeout_seconds(&self) -> Result<Option<u64>> {
self.timeout.map(duration_seconds_ceiling).transpose()
}
}
impl Default for TaskMonitorOptions {
fn default() -> Self {
Self::none()
}
}
fn validate_timeout(timeout: Duration) -> Result<()> {
if timeout.is_zero() {
return Err(Error::invalid_input(
"task monitor timeout must be greater than zero",
));
}
let seconds = duration_seconds_ceiling(timeout)?;
if seconds > i32::MAX as u64 {
return Err(Error::invalid_input(
"task monitor timeout must fit in a Java int",
));
}
Ok(())
}
fn duration_seconds_ceiling(timeout: Duration) -> Result<u64> {
let seconds = timeout.as_secs();
if timeout.subsec_nanos() == 0 {
return Ok(seconds);
}
seconds
.checked_add(1)
.ok_or_else(|| Error::invalid_input("task monitor timeout is too large"))
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use super::TaskMonitorOptions;
#[test]
fn monitor_options_default_to_no_timeout() {
let options = TaskMonitorOptions::default();
assert_eq!(options.timeout_duration(), None);
assert_eq!(options.timeout_seconds().expect("valid timeout"), None);
}
#[test]
fn monitor_options_round_subsecond_timeouts_up() {
let options = TaskMonitorOptions::timeout(Duration::from_millis(1)).expect("valid timeout");
assert_eq!(options.timeout_seconds().expect("valid timeout"), Some(1));
}
#[test]
fn monitor_options_reject_zero_timeout() {
assert!(TaskMonitorOptions::timeout(Duration::ZERO).is_err());
}
#[test]
fn monitor_options_reject_timeouts_larger_than_java_int_seconds() {
let timeout = Duration::from_secs(i32::MAX as u64 + 1);
assert!(TaskMonitorOptions::timeout(timeout).is_err());
}
}