Skip to main content

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}