1use crate::Timecode;
12
13pub trait TimecodeCompare: private::Sealed {
17 fn is_earlier_than(&self, other: &Self) -> bool;
19
20 fn is_later_than(&self, other: &Self) -> bool;
22
23 fn is_same_frame_as(&self, other: &Self) -> bool;
25
26 fn frames_distance(&self, other: &Self) -> u64;
28
29 fn clamp_to_range<'a>(&'a self, lo: &'a Self, hi: &'a Self) -> &'a Self;
33}
34
35mod private {
36 pub trait Sealed {}
37 impl Sealed for super::Timecode {}
38}
39
40impl TimecodeCompare for Timecode {
41 fn is_earlier_than(&self, other: &Timecode) -> bool {
54 self.to_frames() < other.to_frames()
55 }
56
57 fn is_later_than(&self, other: &Timecode) -> bool {
59 self.to_frames() > other.to_frames()
60 }
61
62 fn is_same_frame_as(&self, other: &Timecode) -> bool {
64 self.to_frames() == other.to_frames()
65 }
66
67 fn frames_distance(&self, other: &Timecode) -> u64 {
69 self.to_frames().abs_diff(other.to_frames())
70 }
71
72 fn clamp_to_range<'a>(&'a self, lo: &'a Timecode, hi: &'a Timecode) -> &'a Timecode {
74 if self.is_earlier_than(lo) {
75 lo
76 } else if self.is_later_than(hi) {
77 hi
78 } else {
79 self
80 }
81 }
82}
83
84pub fn is_earlier_than(a: &Timecode, b: &Timecode) -> bool {
88 a.to_frames() < b.to_frames()
89}
90
91pub fn is_later_than(a: &Timecode, b: &Timecode) -> bool {
93 a.to_frames() > b.to_frames()
94}
95
96pub fn is_same_frame(a: &Timecode, b: &Timecode) -> bool {
98 a.to_frames() == b.to_frames()
99}
100
101pub fn earlier<'a>(a: &'a Timecode, b: &'a Timecode) -> &'a Timecode {
103 if a.to_frames() <= b.to_frames() {
104 a
105 } else {
106 b
107 }
108}
109
110pub fn later<'a>(a: &'a Timecode, b: &'a Timecode) -> &'a Timecode {
112 if a.to_frames() >= b.to_frames() {
113 a
114 } else {
115 b
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use crate::FrameRate;
123
124 fn tc(h: u8, m: u8, s: u8, f: u8) -> Timecode {
125 Timecode::new(h, m, s, f, FrameRate::Fps25).expect("valid tc")
126 }
127
128 #[test]
129 fn is_earlier_than_true() {
130 let a = tc(0, 0, 0, 0);
131 let b = tc(0, 0, 1, 0);
132 assert!(a.is_earlier_than(&b));
133 }
134
135 #[test]
136 fn is_earlier_than_false_when_equal() {
137 let a = tc(0, 0, 1, 0);
138 assert!(!a.is_earlier_than(&a));
139 }
140
141 #[test]
142 fn is_later_than_true() {
143 let a = tc(0, 0, 1, 0);
144 let b = tc(0, 0, 0, 0);
145 assert!(a.is_later_than(&b));
146 }
147
148 #[test]
149 fn is_same_frame_as() {
150 let a = tc(1, 2, 3, 4);
151 let b = tc(1, 2, 3, 4);
152 assert!(a.is_same_frame_as(&b));
153 }
154
155 #[test]
156 fn frames_distance() {
157 let a = tc(0, 0, 0, 0);
158 let b = tc(0, 0, 1, 0); assert_eq!(a.frames_distance(&b), 25);
160 }
161
162 #[test]
163 fn clamp_to_range_below() {
164 let lo = tc(0, 0, 1, 0);
165 let hi = tc(0, 0, 5, 0);
166 let x = tc(0, 0, 0, 0); let result = x.clamp_to_range(&lo, &hi);
168 assert!(result.is_same_frame_as(&lo));
169 }
170
171 #[test]
172 fn clamp_to_range_above() {
173 let lo = tc(0, 0, 1, 0);
174 let hi = tc(0, 0, 5, 0);
175 let x = tc(0, 0, 9, 0); let result = x.clamp_to_range(&lo, &hi);
177 assert!(result.is_same_frame_as(&hi));
178 }
179
180 #[test]
181 fn clamp_to_range_within() {
182 let lo = tc(0, 0, 1, 0);
183 let hi = tc(0, 0, 5, 0);
184 let x = tc(0, 0, 3, 0);
185 let result = x.clamp_to_range(&lo, &hi);
186 assert!(result.is_same_frame_as(&x));
187 }
188
189 #[test]
190 fn standalone_is_earlier_than() {
191 assert!(is_earlier_than(&tc(0, 0, 0, 0), &tc(0, 0, 0, 1)));
192 assert!(!is_earlier_than(&tc(0, 0, 0, 1), &tc(0, 0, 0, 0)));
193 }
194
195 #[test]
196 fn standalone_earlier_returns_min() {
197 let a = tc(0, 0, 2, 0);
198 let b = tc(0, 0, 1, 0);
199 assert!(earlier(&a, &b).is_same_frame_as(&b));
200 }
201
202 #[test]
203 fn partial_ord_uses_frame_count() {
204 let a = tc(0, 0, 0, 0);
205 let b = tc(0, 0, 0, 1);
206 assert!(a < b);
207 assert!(b > a);
208 }
209}