Skip to main content

ass_core/parser/script/
auto.rs

1//! Section-context line parsing with automatic type detection.
2//!
3//! Provides [`Script::parse_line_auto`], which infers a line's section from its
4//! prefix, and [`Script::parse_line_in_section`], which parses a line against a
5//! known section type using the script's stored formats.
6
7use alloc::boxed::Box;
8
9use crate::parser::ast::SectionType;
10use crate::parser::errors::ParseError;
11use crate::Result;
12
13use super::types::LineContent;
14use super::Script;
15
16impl<'a> Script<'a> {
17    /// Parse a line based on its section context
18    ///
19    /// Automatically determines the section type from the line content and parses accordingly.
20    ///
21    /// # Arguments
22    ///
23    /// * `line` - The line to parse
24    /// * `line_number` - The line number for error reporting
25    ///
26    /// # Returns
27    ///
28    /// A tuple of (`section_type`, `parsed_content`) or error
29    ///
30    /// # Errors
31    ///
32    /// Returns error if the line format is invalid or section type cannot be determined
33    pub fn parse_line_auto(
34        &self,
35        line: &'a str,
36        line_number: u32,
37    ) -> core::result::Result<(SectionType, LineContent<'a>), ParseError> {
38        let trimmed = line.trim();
39
40        // Try to detect line type
41        if trimmed.starts_with("Style:") {
42            if let Some(style_data) = trimmed.strip_prefix("Style:") {
43                let style = self.parse_style_line_with_context(style_data.trim(), line_number)?;
44                return Ok((SectionType::Styles, LineContent::Style(Box::new(style))));
45            }
46        } else if trimmed.starts_with("Dialogue:")
47            || trimmed.starts_with("Comment:")
48            || trimmed.starts_with("Picture:")
49            || trimmed.starts_with("Sound:")
50            || trimmed.starts_with("Movie:")
51            || trimmed.starts_with("Command:")
52        {
53            let event = self.parse_event_line_with_context(trimmed, line_number)?;
54            return Ok((SectionType::Events, LineContent::Event(Box::new(event))));
55        } else if trimmed.contains(':') && !trimmed.starts_with("Format:") {
56            // Likely a Script Info field
57            if let Some(colon_pos) = trimmed.find(':') {
58                let key = trimmed[..colon_pos].trim();
59                let value = trimmed[colon_pos + 1..].trim();
60                return Ok((SectionType::ScriptInfo, LineContent::Field(key, value)));
61            }
62        }
63
64        Err(ParseError::InvalidFieldFormat {
65            line: line_number as usize,
66        })
67    }
68
69    /// Parse line in section context
70    ///
71    /// Parses a single line knowing its section context, using stored format information.
72    ///
73    /// # Arguments
74    ///
75    /// * `section_type` - The type of section containing this line
76    /// * `line` - The line text to parse
77    /// * `line_number` - Line number for error reporting
78    ///
79    /// # Returns
80    ///
81    /// Parsed line content or error
82    ///
83    /// # Errors
84    ///
85    /// Returns [`ParseError::MissingFormat`] if format information is missing
86    /// Returns other parse errors from line-specific parsers
87    pub fn parse_line_in_section(
88        &self,
89        section_type: SectionType,
90        line: &'a str,
91        line_number: u32,
92    ) -> Result<LineContent<'a>> {
93        match section_type {
94            SectionType::Events => {
95                let format = self
96                    .events_format()
97                    .ok_or(crate::utils::errors::CoreError::Parse(
98                        ParseError::MissingFormat,
99                    ))?;
100                crate::parser::sections::EventsParser::parse_event_line(line, format, line_number)
101                    .map(|event| LineContent::Event(Box::new(event)))
102                    .map_err(crate::utils::errors::CoreError::Parse)
103            }
104            SectionType::Styles => {
105                let format = self
106                    .styles_format()
107                    .ok_or(crate::utils::errors::CoreError::Parse(
108                        ParseError::MissingFormat,
109                    ))?;
110                crate::parser::sections::StylesParser::parse_style_line(line, format, line_number)
111                    .map(|style| LineContent::Style(Box::new(style)))
112                    .map_err(crate::utils::errors::CoreError::Parse)
113            }
114            SectionType::ScriptInfo => {
115                // Parse as key-value field
116                if let Some((key, value)) = line.split_once(':') {
117                    Ok(LineContent::Field(key.trim(), value.trim()))
118                } else {
119                    Err(crate::utils::errors::CoreError::Parse(
120                        ParseError::InvalidFieldFormat {
121                            line: line_number as usize,
122                        },
123                    ))
124                }
125            }
126            _ => Err(crate::utils::errors::CoreError::Parse(
127                ParseError::UnsupportedSection(section_type),
128            )),
129        }
130    }
131}