use crate::error::{BeaverError, BeaverResult};
use crate::listener::WorkListener;
use crate::task::{Task, TaskId};
use crate::work::Work;
use crate::work_fn::BoxWork;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use std::time::Duration;
pub struct RangeIntervalTask {
pub(crate) id: TaskId,
pub(crate) work: BoxWork,
pub(crate) total_retries: u32,
pub(crate) intervals: Box<[u64]>,
pub(crate) tag: Option<String>,
pub(crate) listener: Option<Arc<dyn WorkListener>>,
pub(crate) interrupted: AtomicBool,
}
#[derive(Clone, Debug)]
pub struct RangeIntervalRange {
pub start_inclusive: u32,
pub end_inclusive: u32,
pub duration_millis: u64,
}
pub struct RangeIntervalBuilder {
work: Option<BoxWork>,
total_retries: u32,
ranges: Vec<RangeIntervalRange>,
tag: Option<String>,
listener: Option<Arc<dyn WorkListener>>,
}
impl RangeIntervalBuilder {
pub fn new<W>(work: W, total_retries: u32) -> Self
where
W: Work + Send + 'static,
{
RangeIntervalBuilder {
work: Some(Box::new(work)),
total_retries,
ranges: Vec::new(),
tag: None,
listener: None,
}
}
pub fn add_range(
mut self,
start_inclusive: u32,
end_inclusive: u32,
duration: Duration,
) -> Self {
self.ranges.push(RangeIntervalRange {
start_inclusive,
end_inclusive,
duration_millis: duration.as_millis().min(u64::MAX as u128) as u64,
});
self
}
pub fn tag(mut self, tag: impl Into<String>) -> Self {
self.tag = Some(tag.into());
self
}
pub fn listener(mut self, listener: Arc<dyn WorkListener>) -> Self {
self.listener = Some(listener);
self
}
pub fn build(self) -> BeaverResult<Arc<Task>> {
let work = self.work.ok_or(BeaverError::BuilderMissingField("work"))?;
if self.ranges.len() > self.total_retries as usize {
return Err(BeaverError::RangeIntervalRangesExceedTotal {
total: self.total_retries,
ranges_count: self.ranges.len(),
});
}
let total = self.total_retries as usize;
let mut intervals = vec![0u64; total];
for r in &self.ranges {
let start = r.start_inclusive as usize;
let end = (r.end_inclusive as usize).min(total.saturating_sub(1));
for i in start..=end {
if i < total {
intervals[i] = r.duration_millis;
}
}
}
Ok(Arc::new(Task::RangeInterval(RangeIntervalTask {
id: TaskId::new(),
work,
total_retries: self.total_retries,
intervals: intervals.into_boxed_slice(),
tag: self.tag,
listener: self.listener,
interrupted: AtomicBool::new(false),
})))
}
}