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