oxedyne/fe2o3/fe2o3_datime/src/time/interval.rs
5.1 KiB, 38 runs
created by r1870400018:6389, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | //! [Written with AI entirely](https://need2know.ai/entirely-ai/code)\ |
| 2 | //! Anthropic Claude |
| 3 | |
| 4 | use crate::{ |
| 5 | core::{Interval, Time}, |
| 6 | time::{CalClock, CalClockDuration}, |
| 7 | }; |
| 8 | use oxedyne_fe2o3_core::prelude::*; |
| 9 | |
| 10 | #[derive(Clone, Debug, PartialEq)] |
| 11 | pub struct CalClockInterval { |
| 12 | start: CalClock, |
| 13 | finish: CalClock, |
| 14 | } |
| 15 | |
| 16 | impl CalClockInterval { |
| 17 | pub fn new(start: CalClock, finish: CalClock) -> Outcome<Self> { |
| 18 | // Validate that start is before or equal to finish |
| 19 | if start <= finish { |
| 20 | Ok(Self { start, finish }) |
| 21 | } else { |
| 22 | Err(err!("Start time {} must be before or equal to finish time {}", start, finish; Invalid, Input)) |
| 23 | } |
| 24 | } |
| 25 | |
| 26 | pub fn start(&self) -> &CalClock { |
| 27 | &self.start |
| 28 | } |
| 29 | |
| 30 | pub fn finish(&self) -> &CalClock { |
| 31 | &self.finish |
| 32 | } |
| 33 | |
| 34 | pub fn duration(&self) -> Outcome<CalClockDuration> { |
| 35 | self.start.duration_until(&self.finish) |
| 36 | } |
| 37 | |
| 38 | pub fn contains_time(&self, time: &CalClock) -> bool { |
| 39 | time >= &self.start && time <= &self.finish |
| 40 | } |
| 41 | |
| 42 | pub fn overlaps_with(&self, other: &Self) -> bool { |
| 43 | // Intervals overlap if one starts before the other ends |
| 44 | self.start <= other.finish && other.start <= self.finish |
| 45 | } |
| 46 | |
| 47 | pub fn intersection(&self, other: &Self) -> Option<Self> { |
| 48 | if !self.overlaps_with(other) { |
| 49 | return None; |
| 50 | } |
| 51 | |
| 52 | let intersection_start = if self.start >= other.start { |
| 53 | self.start.clone() |
| 54 | } else { |
| 55 | other.start.clone() |
| 56 | }; |
| 57 | |
| 58 | let intersection_finish = if self.finish <= other.finish { |
| 59 | self.finish.clone() |
| 60 | } else { |
| 61 | other.finish.clone() |
| 62 | }; |
| 63 | |
| 64 | Self::new(intersection_start, intersection_finish).ok() |
| 65 | } |
| 66 | |
| 67 | pub fn union(&self, other: &Self) -> Option<Self> { |
| 68 | if !self.overlaps_with(other) { |
| 69 | return None; |
| 70 | } |
| 71 | |
| 72 | let union_start = if self.start <= other.start { |
| 73 | self.start.clone() |
| 74 | } else { |
| 75 | other.start.clone() |
| 76 | }; |
| 77 | |
| 78 | let union_finish = if self.finish >= other.finish { |
| 79 | self.finish.clone() |
| 80 | } else { |
| 81 | other.finish.clone() |
| 82 | }; |
| 83 | |
| 84 | Self::new(union_start, union_finish).ok() |
| 85 | } |
| 86 | |
| 87 | pub fn is_adjacent_to(&self, other: &Self) -> bool { |
| 88 | self.finish == other.start || other.finish == self.start |
| 89 | } |
| 90 | |
| 91 | pub fn is_before(&self, other: &Self) -> bool { |
| 92 | self.finish < other.start |
| 93 | } |
| 94 | |
| 95 | pub fn is_after(&self, other: &Self) -> bool { |
| 96 | self.start > other.finish |
| 97 | } |
| 98 | |
| 99 | pub fn expand(&self, duration: &CalClockDuration) -> Outcome<Self> { |
| 100 | let new_start = res!(self.start.subtract_duration(duration)); |
| 101 | let new_finish = res!(self.finish.add_duration(duration)); |
| 102 | Self::new(new_start, new_finish) |
| 103 | } |
| 104 | |
| 105 | pub fn contract(&self, duration: &CalClockDuration) -> Outcome<Self> { |
| 106 | let new_start = res!(self.start.add_duration(duration)); |
| 107 | let new_finish = res!(self.finish.subtract_duration(duration)); |
| 108 | |
| 109 | if new_start <= new_finish { |
| 110 | Self::new(new_start, new_finish) |
| 111 | } else { |
| 112 | Err(err!("Cannot contract interval by {:?} - would result in negative duration", duration; Invalid, Input)) |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | pub fn shift(&self, duration: &CalClockDuration) -> Outcome<Self> { |
| 117 | let new_start = res!(self.start.add_duration(duration)); |
| 118 | let new_finish = res!(self.finish.add_duration(duration)); |
| 119 | Self::new(new_start, new_finish) |
| 120 | } |
| 121 | |
| 122 | pub fn split_at(&self, split_time: &CalClock) -> Outcome<(Self, Self)> { |
| 123 | if !self.contains_time(split_time) { |
| 124 | return Err(err!("Split time {} is not within interval", split_time; Invalid, Input)); |
| 125 | } |
| 126 | |
| 127 | let first_interval = res!(Self::new(self.start.clone(), split_time.clone())); |
| 128 | let second_interval = res!(Self::new(split_time.clone(), self.finish.clone())); |
| 129 | |
| 130 | Ok((first_interval, second_interval)) |
| 131 | } |
| 132 | |
| 133 | pub fn midpoint(&self) -> Outcome<CalClock> { |
| 134 | let duration = res!(self.duration()); |
| 135 | let half_duration = res!(duration.divide_by(2)); |
| 136 | self.start.add_duration(&half_duration) |
| 137 | } |
| 138 | |
| 139 | pub fn contains_interval(&self, other: &Self) -> bool { |
| 140 | self.start <= other.start && other.finish <= self.finish |
| 141 | } |
| 142 | |
| 143 | pub fn merge_overlapping(intervals: Vec<Self>) -> Vec<Self> { |
| 144 | if intervals.is_empty() { |
| 145 | return Vec::new(); |
| 146 | } |
| 147 | |
| 148 | let mut sorted_intervals = intervals; |
| 149 | sorted_intervals.sort_by(|a, b| a.start.cmp(&b.start)); |
| 150 | |
| 151 | let mut merged = Vec::new(); |
| 152 | let mut current = sorted_intervals[0].clone(); |
| 153 | |
| 154 | for interval in sorted_intervals.into_iter().skip(1) { |
| 155 | if current.overlaps_with(&interval) || current.is_adjacent_to(&interval) { |
| 156 | // Merge with current |
| 157 | if let Some(union) = current.union(&interval) { |
| 158 | current = union; |
| 159 | } else { |
| 160 | // Handle adjacent case |
| 161 | let new_finish = if current.finish >= interval.finish { |
| 162 | current.finish.clone() |
| 163 | } else { |
| 164 | interval.finish.clone() |
| 165 | }; |
| 166 | current = Self::new(current.start.clone(), new_finish).unwrap_or(current); |
| 167 | } |
| 168 | } else { |
| 169 | // No overlap, add current to result and start new |
| 170 | merged.push(current); |
| 171 | current = interval; |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | merged.push(current); |
| 176 | merged |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | impl Interval<CalClockDuration> for CalClockInterval { |
| 181 | fn get_duration(&self) -> Outcome<CalClockDuration> { |
| 182 | self.duration() |
| 183 | } |
| 184 | |
| 185 | fn contains<T: Time>(&self, _time: &T) -> bool { |
| 186 | // This would need a way to convert T to CalClock for proper comparison |
| 187 | // For now, return false as we can't convert arbitrary Time types |
| 188 | false |
| 189 | } |
| 190 | |
| 191 | fn overlaps(&self, other: &Self) -> bool { |
| 192 | self.overlaps_with(other) |
| 193 | } |
| 194 | } |