Skip to main content

ass_core/parser/incremental/
section_bounds.rs

1//! Section boundary discovery for incremental re-parsing
2
3use crate::parser::errors::ParseError;
4use crate::parser::SectionType;
5
6/// Find the start of a section header before the given position
7///
8/// # Errors
9///
10/// Returns [`ParseError::SectionNotFound`] if the section header cannot be found
11pub fn find_section_header_start(
12    source: &str,
13    start_hint: usize,
14    section_type: SectionType,
15) -> Result<usize, ParseError> {
16    // Expected header for each section type
17    let header = match section_type {
18        SectionType::ScriptInfo => "[Script Info]",
19        SectionType::Styles => "[V4+ Styles]",
20        SectionType::Events => "[Events]",
21        SectionType::Fonts => "[Fonts]",
22        SectionType::Graphics => "[Graphics]",
23    };
24
25    // Search backwards from start_hint for the header
26    let search_start = start_hint.saturating_sub(header.len() + 100); // Look back up to 100 chars
27    let search_text = &source[search_start..start_hint.min(source.len())];
28
29    search_text
30        .rfind(header)
31        .map_or(Err(ParseError::SectionNotFound), |pos| {
32            // Found the header, now find the start of the line
33            let header_pos = search_start + pos;
34            let line_start = source[..header_pos].rfind('\n').map_or(0, |p| p + 1);
35            Ok(line_start)
36        })
37}
38
39/// Find the end of a section (start of next section or end of file)
40///
41/// # Errors
42///
43/// Returns [`ParseError`] if an error occurs while finding the section end
44pub fn find_section_end(
45    source: &str,
46    end_hint: usize,
47    _section_type: SectionType,
48) -> Result<usize, ParseError> {
49    // Look for the next section header
50    let section_headers = [
51        "[Script Info]",
52        "[V4+ Styles]",
53        "[Events]",
54        "[Fonts]",
55        "[Graphics]",
56    ];
57
58    let search_text = &source[end_hint..];
59
60    // Find the nearest section header
61    let mut min_pos = None;
62    for header in &section_headers {
63        if let Some(pos) = search_text.find(header) {
64            min_pos = Some(min_pos.map_or(pos, |min: usize| min.min(pos)));
65        }
66    }
67
68    min_pos.map_or(Ok(source.len()), |pos| {
69        // Found next section, return start of that line
70        let next_section_pos = end_hint + pos;
71        let line_start = source[..next_section_pos].rfind('\n').map_or(0, |p| p + 1);
72        Ok(line_start)
73    })
74}