ass_core/parser/ast/section/section_enum.rs
1//! Top-level section enum with span and validation helpers.
2//!
3//! Defines the [`Section`] enum representing the main sections of an ASS
4//! script along with span computation and zero-copy span validation used
5//! for debugging.
6
7use alloc::vec::Vec;
8
9#[cfg(not(feature = "std"))]
10extern crate alloc;
11
12use super::SectionType;
13use crate::parser::ast::{Event, Font, Graphic, ScriptInfo, Span, Style};
14#[cfg(debug_assertions)]
15use core::ops::Range;
16
17/// Top-level section in an ASS script
18///
19/// Represents the main sections that can appear in an ASS subtitle file.
20/// Each variant contains the parsed content of that section with zero-copy
21/// string references to the original source text.
22///
23/// # Examples
24///
25/// ```rust
26/// use ass_core::parser::ast::{Section, ScriptInfo, SectionType, Span};
27///
28/// let info = ScriptInfo { fields: vec![("Title", "Test")], span: Span::new(0, 10, 1, 1) };
29/// let section = Section::ScriptInfo(info);
30/// assert_eq!(section.section_type(), SectionType::ScriptInfo);
31/// ```
32#[derive(Debug, Clone, PartialEq, Eq)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize))]
34pub enum Section<'a> {
35 /// [Script Info] section with metadata
36 ///
37 /// Contains key-value pairs defining script metadata like title,
38 /// script type, resolution, and other configuration values.
39 ScriptInfo(ScriptInfo<'a>),
40
41 /// [V4+ Styles] section with style definitions
42 ///
43 /// Contains style definitions that can be referenced by events.
44 /// Each style defines font, colors, positioning, and other
45 /// visual properties for subtitle rendering.
46 Styles(Vec<Style<'a>>),
47
48 /// `[Events\]` section with dialogue and commands
49 ///
50 /// Contains dialogue lines, comments, and other timed events
51 /// that make up the actual subtitle content.
52 Events(Vec<Event<'a>>),
53
54 /// `[Fonts\]` section with embedded font data
55 ///
56 /// Contains UU-encoded font files embedded in the script.
57 /// Allows scripts to include custom fonts for portable rendering.
58 Fonts(Vec<Font<'a>>),
59
60 /// `[Graphics\]` section with embedded images
61 ///
62 /// Contains UU-encoded image files embedded in the script.
63 /// Used for logos, textures, and other graphical elements.
64 Graphics(Vec<Graphic<'a>>),
65}
66
67impl Section<'_> {
68 /// Get the span covering this entire section
69 ///
70 /// Computes the span by looking at the content's spans.
71 /// Returns None if the section is empty.
72 #[must_use]
73 pub fn span(&self) -> Option<Span> {
74 match self {
75 Section::ScriptInfo(info) => Some(info.span),
76 Section::Styles(styles) => {
77 if styles.is_empty() {
78 None
79 } else {
80 // Merge first and last style spans
81 let first = &styles[0].span;
82 let last = &styles[styles.len() - 1].span;
83 Some(Span::new(first.start, last.end, first.line, first.column))
84 }
85 }
86 Section::Events(events) => {
87 if events.is_empty() {
88 None
89 } else {
90 // Merge first and last event spans
91 let first = &events[0].span;
92 let last = &events[events.len() - 1].span;
93 Some(Span::new(first.start, last.end, first.line, first.column))
94 }
95 }
96 Section::Fonts(fonts) => {
97 if fonts.is_empty() {
98 None
99 } else {
100 // Merge first and last font spans
101 let first = &fonts[0].span;
102 let last = &fonts[fonts.len() - 1].span;
103 Some(Span::new(first.start, last.end, first.line, first.column))
104 }
105 }
106 Section::Graphics(graphics) => {
107 if graphics.is_empty() {
108 None
109 } else {
110 // Merge first and last graphic spans
111 let first = &graphics[0].span;
112 let last = &graphics[graphics.len() - 1].span;
113 Some(Span::new(first.start, last.end, first.line, first.column))
114 }
115 }
116 }
117 }
118
119 /// Get section type discriminant for efficient matching
120 ///
121 /// Returns the section type without borrowing the section content,
122 /// allowing for efficient type-based filtering and routing.
123 ///
124 /// # Examples
125 ///
126 /// ```rust
127 /// # use ass_core::parser::ast::{Section, ScriptInfo, SectionType, Span};
128 /// let info = Section::ScriptInfo(ScriptInfo { fields: Vec::new(), span: Span::new(0, 0, 0, 0) });
129 /// assert_eq!(info.section_type(), SectionType::ScriptInfo);
130 /// ```
131 #[must_use]
132 pub const fn section_type(&self) -> SectionType {
133 match self {
134 Section::ScriptInfo(_) => SectionType::ScriptInfo,
135 Section::Styles(_) => SectionType::Styles,
136 Section::Events(_) => SectionType::Events,
137 Section::Fonts(_) => SectionType::Fonts,
138 Section::Graphics(_) => SectionType::Graphics,
139 }
140 }
141
142 /// Validate all spans in this section reference valid source
143 ///
144 /// Debug helper to ensure zero-copy invariants are maintained.
145 /// Validates that all string references in the section point to
146 /// memory within the specified source range.
147 ///
148 /// Only available in debug builds to avoid performance overhead
149 /// in release builds.
150 ///
151 /// # Arguments
152 ///
153 /// * `source_range` - Valid memory range for source text
154 ///
155 /// # Returns
156 ///
157 /// `true` if all spans are valid, `false` otherwise
158 #[cfg(debug_assertions)]
159 #[must_use]
160 pub fn validate_spans(&self, source_range: &Range<usize>) -> bool {
161 match self {
162 Section::ScriptInfo(info) => info.validate_spans(source_range),
163 Section::Styles(styles) => styles.iter().all(|s| s.validate_spans(source_range)),
164 Section::Events(events) => events.iter().all(|e| e.validate_spans(source_range)),
165 Section::Fonts(fonts) => fonts.iter().all(|f| f.validate_spans(source_range)),
166 Section::Graphics(graphics) => graphics.iter().all(|g| g.validate_spans(source_range)),
167 }
168 }
169}