use super::{BeyondCalendar, WorkingTime, hours, millis};
impl WorkingTime {
fn at(&self, t: f64) -> Result<i64, BeyondCalendar> {
let t = millis(t).ok_or(BeyondCalendar)?;
if t < self.window.0 || t > self.window.1 {
return Err(BeyondCalendar);
}
Ok(t)
}
fn span(&self, index: usize) -> Result<((i64, i64), i64), BeyondCalendar> {
match (self.spans.get(index), self.before.get(index)) {
(Some(span), Some(before)) => Ok((*span, *before)),
_ => Err(BeyondCalendar),
}
}
fn locate(&self, t: i64) -> Result<(i64, Option<(i64, i64)>), BeyondCalendar> {
let Some(index) = self.starts.through(t).checked_sub(1) else {
return Ok((0, None));
};
let ((start, end), before) = self.span(index)?;
Ok((before + t.min(end) - start, Some((start, end))))
}
fn worked(&self, t: i64) -> Result<i64, BeyondCalendar> {
Ok(self.locate(t)?.0)
}
fn instant(&self, index: usize, target: i64) -> Result<i64, BeyondCalendar> {
let ((start, _), before) = self.span(index)?;
let at = start + (target - before);
if at > self.window.1 {
return Err(BeyondCalendar);
}
Ok(at)
}
pub fn add(&self, t: f64, hours_of_work: f64) -> Result<f64, BeyondCalendar> {
if self.always {
return Ok(t + hours_of_work.max(0.0));
}
let from = self.at(t)?;
let amount = millis(hours_of_work).ok_or(BeyondCalendar)?;
if amount <= 0 {
return Ok(t);
}
let (worked, span) = self.locate(from)?;
if let Some((start, end)) = span
&& from >= start
&& from < end
&& from + amount <= end
{
return Ok(hours(from + amount));
}
let target = worked + amount;
let end = self.instant(self.through.below(target), target)?;
Ok(hours(end.max(from)))
}
pub fn add_late(&self, t: f64, hours_of_work: f64) -> Result<f64, BeyondCalendar> {
if self.always {
return Ok(t + hours_of_work.max(0.0));
}
let from = self.at(t)?;
let amount = millis(hours_of_work).ok_or(BeyondCalendar)?.max(0);
let target = self.worked(from)? + amount;
let end = self.instant(self.through.through(target), target)?;
Ok(hours(end.max(from)))
}
pub fn sub(&self, t: f64, hours_of_work: f64) -> Result<f64, BeyondCalendar> {
if self.always {
return Ok(t - hours_of_work.max(0.0));
}
let to = self.at(t)?;
let amount = millis(hours_of_work).ok_or(BeyondCalendar)?;
if amount <= 0 {
return Ok(t);
}
let (worked, span) = self.locate(to)?;
if let Some((start, end)) = span
&& to > start
&& to <= end
&& to - amount > start
{
return Ok(hours(to - amount));
}
let target = worked - amount;
if target < 0 {
return Err(BeyondCalendar);
}
let start = self.instant(self.through.through(target), target)?;
Ok(hours(start.min(to)))
}
pub fn shift(&self, t: f64, hours_of_work: f64) -> Result<f64, BeyondCalendar> {
if hours_of_work >= 0.0 {
self.add(t, hours_of_work)
} else {
self.sub(t, -hours_of_work)
}
}
pub fn unshift(&self, t: f64, hours_of_work: f64) -> Result<f64, BeyondCalendar> {
if hours_of_work >= 0.0 {
self.sub(t, hours_of_work)
} else {
self.add_late(t, -hours_of_work)
}
}
pub fn align(&self, t: f64) -> Result<f64, BeyondCalendar> {
if self.always {
return Ok(t);
}
let at = self.at(t)?;
let ((start, _), _) = self.span(self.ends.below(at))?;
Ok(if start > at { hours(start) } else { t })
}
pub fn next_working(&self, t: f64) -> Result<f64, BeyondCalendar> {
if self.always {
return Ok(t);
}
let at = self.at(t)?;
let ((start, _), _) = self.span(self.ends.through(at))?;
Ok(if start > at { hours(start) } else { t })
}
pub fn place(&self, t: f64, hours_of_work: f64) -> Result<(f64, f64), BeyondCalendar> {
if self.always {
return Ok((t, t + hours_of_work.max(0.0)));
}
let amount = millis(hours_of_work).ok_or(BeyondCalendar)?;
if amount <= 0 {
return Ok((t, t));
}
let at = self.at(t)?;
let index = self.ends.through(at);
let ((start, end), before) = self.span(index)?;
let begin = start.max(at);
let starts = if begin > at { hours(begin) } else { t };
if begin + amount <= end {
return Ok((starts, hours(begin + amount)));
}
let target = before + (begin - start) + amount;
let finish = self.instant(self.through.below(target), target)?;
Ok((starts, hours(finish.max(begin))))
}
pub fn retreat(&self, t: f64, hours_of_work: f64) -> Result<f64, BeyondCalendar> {
if self.always {
return Ok(t - hours_of_work.max(0.0));
}
let at = self.at(t)?;
let index = self
.starts
.through(at)
.checked_sub(1)
.ok_or(BeyondCalendar)?;
let ((start, end), before) = self.span(index)?;
let to = at.min(end);
let amount = millis(hours_of_work).ok_or(BeyondCalendar)?;
if amount <= 0 {
return Ok(if end < at { hours(end) } else { t });
}
if to - amount > start {
return Ok(hours(to - amount));
}
let target = before + (to - start) - amount;
if target < 0 {
return Err(BeyondCalendar);
}
let begin = self.instant(self.through.through(target), target)?;
Ok(hours(begin.min(to)))
}
pub fn align_back(&self, t: f64) -> Result<f64, BeyondCalendar> {
if self.always {
return Ok(t);
}
let at = self.at(t)?;
let index = self
.starts
.through(at)
.checked_sub(1)
.ok_or(BeyondCalendar)?;
let ((_, end), _) = self.span(index)?;
Ok(if end < at { hours(end) } else { t })
}
pub fn between(&self, a: f64, b: f64) -> Result<f64, BeyondCalendar> {
if self.always {
return Ok((b - a).max(0.0));
}
let worked = self.worked(self.at(b)?)? - self.worked(self.at(a)?)?;
Ok(hours(worked.max(0)))
}
}