Skip to main content

motion_core/authoring/timeline/
diagnostics.rs

1//! Non-fatal observations recorded while building a timeline.
2
3use crate::{Duration, Time};
4use std::sync::Arc;
5
6/// Boundary that moved when scheduling onto the timeline's frame grid.
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8pub enum BoundaryEdge {
9    /// Clip activation start.
10    Start,
11    /// Clip exclusive end.
12    End,
13    /// Trailing track cursor, including an explicit gap.
14    Cursor,
15    /// Explicit timeline duration override.
16    Duration,
17}
18
19/// Structured warning; construction never writes warnings to stderr.
20#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct BoundaryAdjustment {
22    /// Track insertion index, absent for a timeline duration override.
23    pub track: Option<usize>,
24    /// Stable clip insertion ID, absent for cursors or duration overrides.
25    pub clip: Option<usize>,
26    /// Adjusted boundary category.
27    pub edge: BoundaryEdge,
28    /// Original tick position.
29    pub requested: Time,
30    /// Nearest representable frame boundary; ties choose the later frame.
31    pub aligned: Time,
32    /// Exact aligned frame index.
33    pub frame: i64,
34}
35
36/// One non-fatal observation about a built timeline.
37#[derive(Clone, Debug, PartialEq, Eq)]
38#[non_exhaustive]
39pub enum TimelineWarning {
40    /// A boundary moved onto the frame grid.
41    Boundary(BoundaryAdjustment),
42    /// The explicit duration is shorter than the content, so the picture is cut
43    /// off. Audio is not trimmed; the output range limits it.
44    Truncated {
45        /// Explicit, frame-aligned duration.
46        duration: Duration,
47        /// Frame-aligned end of the tracks and audio.
48        content: Duration,
49    },
50}
51
52/// Warnings collected by a timeline builder; fatal errors are returned by `build`.
53#[derive(Clone, Debug, Default, PartialEq, Eq)]
54pub struct Diagnostics {
55    warnings: Arc<Vec<TimelineWarning>>,
56}
57
58impl Diagnostics {
59    pub(super) fn new(warnings: Vec<TimelineWarning>) -> Self {
60        Self {
61            warnings: Arc::new(warnings),
62        }
63    }
64
65    /// All warnings in the order they were recorded.
66    pub fn warnings(&self) -> &[TimelineWarning] {
67        &self.warnings
68    }
69
70    /// Frame-grid adjustments, without other warning kinds.
71    pub fn boundary_adjustments(&self) -> impl Iterator<Item = &BoundaryAdjustment> {
72        self.warnings.iter().filter_map(|warning| match warning {
73            TimelineWarning::Boundary(adjustment) => Some(adjustment),
74            _ => None,
75        })
76    }
77
78    /// Whether the timeline built without any warning.
79    pub fn is_empty(&self) -> bool {
80        self.warnings.is_empty()
81    }
82}