Skip to main content

spatialrust_episode/
annotate.rs

1//! Time-ranged annotation layers.
2
3use crate::{EpisodeError, EpisodeResult};
4
5/// Annotation payload type.
6#[derive(Clone, Debug, PartialEq, Eq, Hash)]
7pub enum AnnotationKind {
8    /// Bounding / free-form label.
9    Label(String),
10    /// Numeric scalar annotation.
11    Scalar(String),
12}
13
14/// Inclusive annotation time span in nanoseconds.
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
16pub struct AnnotationSpan {
17    /// Start nanos.
18    pub start_ns: u64,
19    /// End nanos.
20    pub end_ns: u64,
21}
22
23impl AnnotationSpan {
24    /// Creates a validated span.
25    pub fn try_new(start_ns: u64, end_ns: u64) -> EpisodeResult<Self> {
26        if end_ns < start_ns {
27            return Err(EpisodeError::InvalidConfiguration("annotation span end < start".into()));
28        }
29        Ok(Self { start_ns, end_ns })
30    }
31}
32
33/// Named annotation layer over an episode.
34#[derive(Clone, Debug, PartialEq)]
35pub struct AnnotationLayer {
36    /// Layer name.
37    pub name: String,
38    /// Annotations.
39    pub items: Vec<(AnnotationSpan, AnnotationKind)>,
40}
41
42impl AnnotationLayer {
43    /// Creates an empty named layer.
44    #[must_use]
45    pub fn new(name: impl Into<String>) -> Self {
46        Self { name: name.into(), items: Vec::new() }
47    }
48
49    /// Pushes an annotation item.
50    pub fn push(&mut self, span: AnnotationSpan, kind: AnnotationKind) {
51        self.items.push((span, kind));
52    }
53}