pub type EdlResult<T> = Result<T, EdlError>;
#[derive(Debug, thiserror::Error)]
pub enum EdlError {
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("Parse error at line {line}: {message}")]
Parse {
line: usize,
message: String,
},
#[error("Invalid timecode at line {line}: {message}")]
InvalidTimecode {
line: usize,
message: String,
},
#[error("Invalid event number: {0}")]
InvalidEventNumber(u32),
#[error("Invalid edit type: {0}")]
InvalidEditType(String),
#[error("Invalid track type: {0}")]
InvalidTrackType(String),
#[error("Invalid transition duration: {0}")]
InvalidTransitionDuration(String),
#[error("Invalid reel name: {0}")]
InvalidReelName(String),
#[error("Invalid motion effect: {0}")]
InvalidMotionEffect(String),
#[error("Invalid audio channel: {0}")]
InvalidAudioChannel(String),
#[error("Unsupported EDL format: {0}")]
UnsupportedFormat(String),
#[error("Validation error: {0}")]
ValidationError(String),
#[error("Event {0} not found")]
EventNotFound(u32),
#[error("Timecode out of range: {0}")]
TimecodeOutOfRange(String),
#[error("Invalid frame rate: {0}")]
InvalidFrameRate(String),
#[error("Invalid drop frame mode: {0}")]
InvalidDropFrameMode(String),
#[error("Comment parsing error at line {line}: {message}")]
CommentError {
line: usize,
message: String,
},
#[error("Event overlap detected: event {event1} overlaps with event {event2}")]
EventOverlap {
event1: u32,
event2: u32,
},
#[error("Gap in timeline detected between event {event1} and event {event2}")]
TimelineGap {
event1: u32,
event2: u32,
},
#[error("Invalid wipe pattern: {0}")]
InvalidWipePattern(String),
#[error("Invalid key type: {0}")]
InvalidKeyType(String),
#[error("Conversion error: {0}")]
ConversionError(String),
#[error("Missing required field: {0}")]
MissingField(String),
#[error("Invalid source timecode range: source out must be greater than source in")]
InvalidSourceRange,
#[error("Invalid record timecode range: record out must be greater than record in")]
InvalidRecordRange,
}
impl EdlError {
#[must_use]
pub fn parse(line: usize, message: impl Into<String>) -> Self {
Self::Parse {
line,
message: message.into(),
}
}
#[must_use]
pub fn invalid_timecode(line: usize, message: impl Into<String>) -> Self {
Self::InvalidTimecode {
line,
message: message.into(),
}
}
#[must_use]
pub fn comment_error(line: usize, message: impl Into<String>) -> Self {
Self::CommentError {
line,
message: message.into(),
}
}
#[must_use]
pub fn validation(message: impl Into<String>) -> Self {
Self::ValidationError(message.into())
}
#[must_use]
pub const fn event_overlap(event1: u32, event2: u32) -> Self {
Self::EventOverlap { event1, event2 }
}
#[must_use]
pub const fn timeline_gap(event1: u32, event2: u32) -> Self {
Self::TimelineGap { event1, event2 }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_error() {
let err = EdlError::parse(42, "Invalid syntax");
assert!(matches!(err, EdlError::Parse { line: 42, .. }));
let msg = format!("{err}");
assert!(msg.contains("42"));
assert!(msg.contains("Invalid syntax"));
}
#[test]
fn test_invalid_timecode() {
let err = EdlError::invalid_timecode(10, "Out of range");
assert!(matches!(err, EdlError::InvalidTimecode { line: 10, .. }));
}
#[test]
fn test_validation_error() {
let err = EdlError::validation("Timeline gap detected");
assert!(matches!(err, EdlError::ValidationError(_)));
}
#[test]
fn test_event_overlap() {
let err = EdlError::event_overlap(1, 2);
assert!(matches!(
err,
EdlError::EventOverlap {
event1: 1,
event2: 2
}
));
}
}