Skip to main content

oximedia_edl/
error.rs

1//! Error types for EDL operations.
2
3/// Result type for EDL operations.
4pub type EdlResult<T> = Result<T, EdlError>;
5
6/// Errors that can occur during EDL parsing, generation, and validation.
7#[derive(Debug, thiserror::Error)]
8pub enum EdlError {
9    /// I/O error occurred.
10    #[error("I/O error: {0}")]
11    Io(#[from] std::io::Error),
12
13    /// Parse error at a specific line.
14    #[error("Parse error at line {line}: {message}")]
15    Parse {
16        /// Line number where the error occurred.
17        line: usize,
18        /// Description of the parse error.
19        message: String,
20    },
21
22    /// Invalid timecode encountered.
23    #[error("Invalid timecode at line {line}: {message}")]
24    InvalidTimecode {
25        /// Line number where the error occurred.
26        line: usize,
27        /// Description of the timecode error.
28        message: String,
29    },
30
31    /// Invalid event number.
32    #[error("Invalid event number: {0}")]
33    InvalidEventNumber(u32),
34
35    /// Invalid edit type.
36    #[error("Invalid edit type: {0}")]
37    InvalidEditType(String),
38
39    /// Invalid track type.
40    #[error("Invalid track type: {0}")]
41    InvalidTrackType(String),
42
43    /// Invalid transition duration.
44    #[error("Invalid transition duration: {0}")]
45    InvalidTransitionDuration(String),
46
47    /// Invalid reel name.
48    #[error("Invalid reel name: {0}")]
49    InvalidReelName(String),
50
51    /// Invalid motion effect.
52    #[error("Invalid motion effect: {0}")]
53    InvalidMotionEffect(String),
54
55    /// Invalid audio channel.
56    #[error("Invalid audio channel: {0}")]
57    InvalidAudioChannel(String),
58
59    /// Unsupported EDL format.
60    #[error("Unsupported EDL format: {0}")]
61    UnsupportedFormat(String),
62
63    /// EDL validation error.
64    #[error("Validation error: {0}")]
65    ValidationError(String),
66
67    /// Event not found.
68    #[error("Event {0} not found")]
69    EventNotFound(u32),
70
71    /// Timecode out of range.
72    #[error("Timecode out of range: {0}")]
73    TimecodeOutOfRange(String),
74
75    /// Invalid frame rate.
76    #[error("Invalid frame rate: {0}")]
77    InvalidFrameRate(String),
78
79    /// Invalid drop frame mode.
80    #[error("Invalid drop frame mode: {0}")]
81    InvalidDropFrameMode(String),
82
83    /// Comment parsing error.
84    #[error("Comment parsing error at line {line}: {message}")]
85    CommentError {
86        /// Line number where the error occurred.
87        line: usize,
88        /// Description of the comment error.
89        message: String,
90    },
91
92    /// Event overlap detected.
93    #[error("Event overlap detected: event {event1} overlaps with event {event2}")]
94    EventOverlap {
95        /// First overlapping event.
96        event1: u32,
97        /// Second overlapping event.
98        event2: u32,
99    },
100
101    /// Gap in timeline detected.
102    #[error("Gap in timeline detected between event {event1} and event {event2}")]
103    TimelineGap {
104        /// Event before the gap.
105        event1: u32,
106        /// Event after the gap.
107        event2: u32,
108    },
109
110    /// Invalid wipe pattern.
111    #[error("Invalid wipe pattern: {0}")]
112    InvalidWipePattern(String),
113
114    /// Invalid key type.
115    #[error("Invalid key type: {0}")]
116    InvalidKeyType(String),
117
118    /// Conversion error between EDL formats.
119    #[error("Conversion error: {0}")]
120    ConversionError(String),
121
122    /// Missing required field.
123    #[error("Missing required field: {0}")]
124    MissingField(String),
125
126    /// Invalid source timecode range.
127    #[error("Invalid source timecode range: source out must be greater than source in")]
128    InvalidSourceRange,
129
130    /// Invalid record timecode range.
131    #[error("Invalid record timecode range: record out must be greater than record in")]
132    InvalidRecordRange,
133}
134
135impl EdlError {
136    /// Creates a new parse error at the given line.
137    #[must_use]
138    pub fn parse(line: usize, message: impl Into<String>) -> Self {
139        Self::Parse {
140            line,
141            message: message.into(),
142        }
143    }
144
145    /// Creates a new invalid timecode error.
146    #[must_use]
147    pub fn invalid_timecode(line: usize, message: impl Into<String>) -> Self {
148        Self::InvalidTimecode {
149            line,
150            message: message.into(),
151        }
152    }
153
154    /// Creates a new comment error.
155    #[must_use]
156    pub fn comment_error(line: usize, message: impl Into<String>) -> Self {
157        Self::CommentError {
158            line,
159            message: message.into(),
160        }
161    }
162
163    /// Creates a new validation error.
164    #[must_use]
165    pub fn validation(message: impl Into<String>) -> Self {
166        Self::ValidationError(message.into())
167    }
168
169    /// Creates a new event overlap error.
170    #[must_use]
171    pub const fn event_overlap(event1: u32, event2: u32) -> Self {
172        Self::EventOverlap { event1, event2 }
173    }
174
175    /// Creates a new timeline gap error.
176    #[must_use]
177    pub const fn timeline_gap(event1: u32, event2: u32) -> Self {
178        Self::TimelineGap { event1, event2 }
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185
186    #[test]
187    fn test_parse_error() {
188        let err = EdlError::parse(42, "Invalid syntax");
189        assert!(matches!(err, EdlError::Parse { line: 42, .. }));
190        let msg = format!("{err}");
191        assert!(msg.contains("42"));
192        assert!(msg.contains("Invalid syntax"));
193    }
194
195    #[test]
196    fn test_invalid_timecode() {
197        let err = EdlError::invalid_timecode(10, "Out of range");
198        assert!(matches!(err, EdlError::InvalidTimecode { line: 10, .. }));
199    }
200
201    #[test]
202    fn test_validation_error() {
203        let err = EdlError::validation("Timeline gap detected");
204        assert!(matches!(err, EdlError::ValidationError(_)));
205    }
206
207    #[test]
208    fn test_event_overlap() {
209        let err = EdlError::event_overlap(1, 2);
210        assert!(matches!(
211            err,
212            EdlError::EventOverlap {
213                event1: 1,
214                event2: 2
215            }
216        ));
217    }
218}