ass_core/parser/ast/event/event_struct.rs
1//! `Event` struct with construction defaults and timing accessors
2//!
3//! Defines the zero-copy [`Event`] node for the `[Events]` section together
4//! with its dialogue/comment predicates, centisecond time helpers, and the
5//! [`Default`] template implementation.
6
7use super::{EventType, Span};
8
9/// Event from `[Events\]` section (dialogue, comments, etc.)
10///
11/// Represents a single event in the subtitle timeline. Events can be dialogue
12/// lines, comments, or other commands with associated timing and styling.
13/// All fields use zero-copy string references for maximum efficiency.
14///
15/// # Examples
16///
17/// ```rust
18/// use ass_core::parser::ast::{Event, EventType};
19///
20/// let event = Event {
21/// event_type: EventType::Dialogue,
22/// start: "0:00:05.00",
23/// end: "0:00:10.00",
24/// text: "Hello, world!",
25/// ..Event::default()
26/// };
27///
28/// assert!(event.is_dialogue());
29/// ```
30#[derive(Debug, Clone, PartialEq, Eq)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize))]
32pub struct Event<'a> {
33 /// Event type (Dialogue, Comment, etc.)
34 pub event_type: EventType,
35
36 /// Layer for drawing order (higher layers drawn on top)
37 pub layer: &'a str,
38
39 /// Start time in ASS time format (H:MM:SS.CS)
40 pub start: &'a str,
41
42 /// End time in ASS time format (H:MM:SS.CS)
43 pub end: &'a str,
44
45 /// Style name reference
46 pub style: &'a str,
47
48 /// Character name or speaker
49 pub name: &'a str,
50
51 /// Left margin override (pixels)
52 pub margin_l: &'a str,
53
54 /// Right margin override (pixels)
55 pub margin_r: &'a str,
56
57 /// Vertical margin override (pixels) (V4+)
58 pub margin_v: &'a str,
59
60 /// Top margin override (pixels) (V4++)
61 pub margin_t: Option<&'a str>,
62
63 /// Bottom margin override (pixels) (V4++)
64 pub margin_b: Option<&'a str>,
65
66 /// Effect specification for special rendering
67 pub effect: &'a str,
68
69 /// Text content with possible style overrides
70 pub text: &'a str,
71
72 /// Span in source text where this event is defined
73 pub span: Span,
74}
75
76impl Event<'_> {
77 /// Check if this is a dialogue event
78 ///
79 /// Returns `true` for events that should be displayed during playback.
80 #[must_use]
81 pub const fn is_dialogue(&self) -> bool {
82 matches!(self.event_type, EventType::Dialogue)
83 }
84
85 /// Check if this is a comment event
86 ///
87 /// Returns `true` for events that are comments and not displayed.
88 #[must_use]
89 pub const fn is_comment(&self) -> bool {
90 matches!(self.event_type, EventType::Comment)
91 }
92
93 /// Parse start time to centiseconds
94 ///
95 /// Converts the start time string to centiseconds for timing calculations.
96 /// Uses the standard ASS time format parser.
97 ///
98 /// # Errors
99 ///
100 /// Returns an error if the time format is invalid or cannot be parsed.
101 pub fn start_time_cs(&self) -> Result<u32, crate::utils::CoreError> {
102 crate::utils::parse_ass_time(self.start)
103 }
104
105 /// Parse end time to centiseconds
106 ///
107 /// Converts the end time string to centiseconds for timing calculations.
108 /// Uses the standard ASS time format parser.
109 ///
110 /// # Errors
111 ///
112 /// Returns an error if the time format is invalid or cannot be parsed.
113 pub fn end_time_cs(&self) -> Result<u32, crate::utils::CoreError> {
114 crate::utils::parse_ass_time(self.end)
115 }
116
117 /// Get duration in centiseconds
118 ///
119 /// Calculates the event duration by subtracting start time from end time.
120 /// Returns 0 if start time is greater than end time.
121 ///
122 /// # Errors
123 ///
124 /// Returns an error if either start or end time format is invalid.
125 pub fn duration_cs(&self) -> Result<u32, crate::utils::CoreError> {
126 let start = self.start_time_cs()?;
127 let end = self.end_time_cs()?;
128 Ok(end.saturating_sub(start))
129 }
130}
131
132impl Default for Event<'_> {
133 /// Create default event with safe initial values
134 ///
135 /// Provides a valid default event that can be used as a template
136 /// or for testing purposes.
137 fn default() -> Self {
138 Self {
139 event_type: EventType::Dialogue,
140 layer: "0",
141 start: "0:00:00.00",
142 end: "0:00:00.00",
143 style: "Default",
144 name: "",
145 margin_l: "0",
146 margin_r: "0",
147 margin_v: "0",
148 margin_t: None,
149 margin_b: None,
150 effect: "",
151 text: "",
152 span: Span::new(0, 0, 0, 0),
153 }
154 }
155}