1use std::collections::BTreeMap;
7
8pub type MarkerId = u64;
10
11#[derive(Clone, Debug)]
13pub struct Marker {
14 pub id: MarkerId,
16 pub position: i64,
18 pub marker_type: MarkerType,
20 pub name: String,
22 pub color: Option<[u8; 3]>,
24 pub notes: Option<String>,
26}
27
28impl Marker {
29 #[must_use]
31 pub fn new(id: MarkerId, position: i64, name: String) -> Self {
32 Self {
33 id,
34 position,
35 marker_type: MarkerType::Standard,
36 name,
37 color: None,
38 notes: None,
39 }
40 }
41
42 #[must_use]
44 pub fn chapter(id: MarkerId, position: i64, name: String) -> Self {
45 Self {
46 id,
47 position,
48 marker_type: MarkerType::Chapter,
49 name,
50 color: Some([0, 100, 200]),
51 notes: None,
52 }
53 }
54
55 #[must_use]
57 pub fn comment(id: MarkerId, position: i64, name: String, comment: String) -> Self {
58 Self {
59 id,
60 position,
61 marker_type: MarkerType::Comment,
62 name,
63 color: Some([255, 200, 0]),
64 notes: Some(comment),
65 }
66 }
67}
68
69#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71pub enum MarkerType {
72 Standard,
74 Chapter,
76 Comment,
78 In,
80 Out,
82 Cue,
84 Beat,
86}
87
88#[derive(Clone, Debug)]
90pub struct Region {
91 pub id: u64,
93 pub start: i64,
95 pub end: i64,
97 pub name: String,
99 pub color: Option<[u8; 3]>,
101 pub notes: Option<String>,
103 pub locked: bool,
105}
106
107impl Region {
108 #[must_use]
110 pub fn new(id: u64, start: i64, end: i64, name: String) -> Self {
111 Self {
112 id,
113 start,
114 end,
115 name,
116 color: None,
117 notes: None,
118 locked: false,
119 }
120 }
121
122 #[must_use]
124 pub fn duration(&self) -> i64 {
125 self.end - self.start
126 }
127
128 #[must_use]
130 pub fn contains(&self, position: i64) -> bool {
131 position >= self.start && position < self.end
132 }
133
134 #[must_use]
136 pub fn overlaps(&self, other: &Region) -> bool {
137 !(self.end <= other.start || self.start >= other.end)
138 }
139
140 #[must_use]
142 pub fn overlaps_range(&self, start: i64, end: i64) -> bool {
143 !(self.end <= start || self.start >= end)
144 }
145}
146
147#[derive(Debug, Default)]
149pub struct MarkerManager {
150 markers: BTreeMap<i64, Vec<Marker>>,
152 next_id: MarkerId,
154}
155
156impl MarkerManager {
157 #[must_use]
159 pub fn new() -> Self {
160 Self {
161 markers: BTreeMap::new(),
162 next_id: 1,
163 }
164 }
165
166 pub fn add(&mut self, mut marker: Marker) -> MarkerId {
168 marker.id = self.next_id;
169 self.next_id += 1;
170
171 self.markers
172 .entry(marker.position)
173 .or_default()
174 .push(marker.clone());
175
176 marker.id
177 }
178
179 pub fn add_at(&mut self, position: i64, name: String) -> MarkerId {
181 let marker = Marker::new(self.next_id, position, name);
182 self.add(marker)
183 }
184
185 pub fn remove(&mut self, id: MarkerId) -> Option<Marker> {
187 for markers in self.markers.values_mut() {
188 if let Some(pos) = markers.iter().position(|m| m.id == id) {
189 return Some(markers.remove(pos));
190 }
191 }
192 None
193 }
194
195 #[must_use]
197 pub fn get(&self, id: MarkerId) -> Option<&Marker> {
198 self.markers
199 .values()
200 .flat_map(|v| v.iter())
201 .find(|m| m.id == id)
202 }
203
204 #[must_use]
206 pub fn get_at(&self, position: i64) -> Vec<&Marker> {
207 self.markers
208 .get(&position)
209 .map(|v| v.iter().collect())
210 .unwrap_or_default()
211 }
212
213 #[must_use]
215 pub fn get_in_range(&self, start: i64, end: i64) -> Vec<&Marker> {
216 self.markers
217 .range(start..end)
218 .flat_map(|(_, markers)| markers.iter())
219 .collect()
220 }
221
222 #[must_use]
224 pub fn all(&self) -> Vec<&Marker> {
225 self.markers.values().flat_map(|v| v.iter()).collect()
226 }
227
228 #[must_use]
230 pub fn get_by_type(&self, marker_type: MarkerType) -> Vec<&Marker> {
231 self.all()
232 .into_iter()
233 .filter(|m| m.marker_type == marker_type)
234 .collect()
235 }
236
237 #[must_use]
239 pub fn get_chapters(&self) -> Vec<&Marker> {
240 self.get_by_type(MarkerType::Chapter)
241 }
242
243 #[must_use]
245 pub fn find_nearest(&self, position: i64) -> Option<&Marker> {
246 let mut nearest: Option<&Marker> = None;
247 let mut min_distance = i64::MAX;
248
249 for marker in self.all() {
250 let distance = (marker.position - position).abs();
251 if distance < min_distance {
252 min_distance = distance;
253 nearest = Some(marker);
254 }
255 }
256
257 nearest
258 }
259
260 #[must_use]
262 pub fn find_next(&self, position: i64) -> Option<&Marker> {
263 self.markers
264 .range(position + 1..)
265 .flat_map(|(_, markers)| markers.iter())
266 .next()
267 }
268
269 #[must_use]
271 pub fn find_previous(&self, position: i64) -> Option<&Marker> {
272 self.markers
273 .range(..position)
274 .rev()
275 .flat_map(|(_, markers)| markers.iter())
276 .next()
277 }
278
279 pub fn clear(&mut self) {
281 self.markers.clear();
282 }
283
284 #[must_use]
286 pub fn len(&self) -> usize {
287 self.markers.values().map(Vec::len).sum()
288 }
289
290 #[must_use]
292 pub fn is_empty(&self) -> bool {
293 self.markers.is_empty()
294 }
295}
296
297#[derive(Debug, Default)]
299pub struct RegionManager {
300 regions: Vec<Region>,
302 next_id: u64,
304}
305
306impl RegionManager {
307 #[must_use]
309 pub fn new() -> Self {
310 Self {
311 regions: Vec::new(),
312 next_id: 1,
313 }
314 }
315
316 pub fn add(&mut self, mut region: Region) -> u64 {
318 region.id = self.next_id;
319 self.next_id += 1;
320 let id = region.id;
321 self.regions.push(region);
322 id
323 }
324
325 pub fn add_range(&mut self, start: i64, end: i64, name: String) -> u64 {
327 let region = Region::new(self.next_id, start, end, name);
328 self.add(region)
329 }
330
331 pub fn remove(&mut self, id: u64) -> Option<Region> {
333 if let Some(pos) = self.regions.iter().position(|r| r.id == id) {
334 Some(self.regions.remove(pos))
335 } else {
336 None
337 }
338 }
339
340 #[must_use]
342 pub fn get(&self, id: u64) -> Option<&Region> {
343 self.regions.iter().find(|r| r.id == id)
344 }
345
346 pub fn get_mut(&mut self, id: u64) -> Option<&mut Region> {
348 self.regions.iter_mut().find(|r| r.id == id)
349 }
350
351 #[must_use]
353 pub fn all(&self) -> Vec<&Region> {
354 self.regions.iter().collect()
355 }
356
357 #[must_use]
359 pub fn get_at(&self, position: i64) -> Vec<&Region> {
360 self.regions
361 .iter()
362 .filter(|r| r.contains(position))
363 .collect()
364 }
365
366 #[must_use]
368 pub fn get_in_range(&self, start: i64, end: i64) -> Vec<&Region> {
369 self.regions
370 .iter()
371 .filter(|r| r.overlaps_range(start, end))
372 .collect()
373 }
374
375 pub fn clear(&mut self) {
377 self.regions.clear();
378 }
379
380 #[must_use]
382 pub fn len(&self) -> usize {
383 self.regions.len()
384 }
385
386 #[must_use]
388 pub fn is_empty(&self) -> bool {
389 self.regions.is_empty()
390 }
391}
392
393#[derive(Clone, Copy, Debug, Default)]
395pub struct InOutPoints {
396 pub in_point: Option<i64>,
398 pub out_point: Option<i64>,
400}
401
402impl InOutPoints {
403 #[must_use]
405 pub fn new() -> Self {
406 Self {
407 in_point: None,
408 out_point: None,
409 }
410 }
411
412 pub fn set_in(&mut self, position: i64) {
414 self.in_point = Some(position);
415 }
416
417 pub fn set_out(&mut self, position: i64) {
419 self.out_point = Some(position);
420 }
421
422 pub fn clear_in(&mut self) {
424 self.in_point = None;
425 }
426
427 pub fn clear_out(&mut self) {
429 self.out_point = None;
430 }
431
432 pub fn clear(&mut self) {
434 self.in_point = None;
435 self.out_point = None;
436 }
437
438 #[must_use]
440 pub fn is_complete(&self) -> bool {
441 self.in_point.is_some() && self.out_point.is_some()
442 }
443
444 #[must_use]
446 pub fn duration(&self) -> Option<i64> {
447 match (self.in_point, self.out_point) {
448 (Some(i), Some(o)) if o > i => Some(o - i),
449 _ => None,
450 }
451 }
452
453 #[must_use]
455 pub fn range(&self) -> Option<(i64, i64)> {
456 match (self.in_point, self.out_point) {
457 (Some(i), Some(o)) if o > i => Some((i, o)),
458 _ => None,
459 }
460 }
461
462 #[must_use]
464 pub fn contains(&self, position: i64) -> bool {
465 match (self.in_point, self.out_point) {
466 (Some(i), Some(o)) => position >= i && position < o,
467 _ => false,
468 }
469 }
470}