Skip to main content

ass_editor/core/document/
ass_api.rs

1//! ASS-aware query helpers and simple field/event editing
2//!
3//! Convenience methods that parse the document on demand to count or inspect
4//! sections, find event text, and read or update script-info fields without
5//! the caller managing the parser directly.
6
7use super::EditorDocument;
8use crate::core::errors::Result;
9use crate::core::position::{Position, Range};
10use ass_core::parser::ast::Section;
11
12#[cfg(not(feature = "std"))]
13use alloc::{
14    format,
15    string::{String, ToString},
16    vec::Vec,
17};
18
19impl EditorDocument {
20    // === ASS-Aware APIs ===
21
22    /// Get number of events without manual parsing
23    pub fn events_count(&self) -> Result<usize> {
24        self.parse_script_with(|script| {
25            let mut count = 0;
26            for section in script.sections() {
27                if let Section::Events(events) = section {
28                    count += events.len();
29                }
30            }
31            count
32        })
33    }
34
35    /// Get number of styles without manual parsing
36    pub fn styles_count(&self) -> Result<usize> {
37        self.parse_script_with(|script| {
38            let mut count = 0;
39            for section in script.sections() {
40                if let Section::Styles(styles) = section {
41                    count += styles.len();
42                }
43            }
44            count
45        })
46    }
47
48    /// Get script info field names
49    pub fn script_info_fields(&self) -> Result<Vec<String>> {
50        self.parse_script_with(|script| {
51            let mut fields = Vec::new();
52            for section in script.sections() {
53                if let Section::ScriptInfo(info) = section {
54                    for (key, _) in &info.fields {
55                        fields.push(key.to_string());
56                    }
57                }
58            }
59            fields
60        })
61    }
62
63    /// Get number of sections
64    pub fn sections_count(&self) -> Result<usize> {
65        self.parse_script_with(|script| script.sections().len())
66    }
67
68    /// Check if document has events section
69    pub fn has_events(&self) -> Result<bool> {
70        self.parse_script_with(|script| {
71            script
72                .sections()
73                .iter()
74                .any(|section| matches!(section, Section::Events(_)))
75        })
76    }
77
78    /// Check if document has styles section
79    pub fn has_styles(&self) -> Result<bool> {
80        self.parse_script_with(|script| {
81            script
82                .sections()
83                .iter()
84                .any(|section| matches!(section, Section::Styles(_)))
85        })
86    }
87
88    /// Get event text by line pattern (simplified search)
89    pub fn find_event_text(&self, pattern: &str) -> Result<Vec<String>> {
90        self.parse_script_with(|script| {
91            let mut matches = Vec::new();
92            for section in script.sections() {
93                if let Section::Events(events) = section {
94                    for event in events {
95                        if event.text.contains(pattern) {
96                            matches.push(event.text.to_string());
97                        }
98                    }
99                }
100            }
101            matches
102        })
103    }
104
105    /// Edit an event by finding and replacing text (simplified ASS-aware editing)
106    pub fn edit_event_text(&mut self, old_text: &str, new_text: &str) -> Result<()> {
107        let content = self.text();
108
109        if let Some(pos) = content.find(old_text) {
110            let range = Range::new(Position::new(pos), Position::new(pos + old_text.len()));
111            self.replace(range, new_text)?;
112        }
113
114        Ok(())
115    }
116
117    /// Get script info field value by key
118    pub fn get_script_info_field(&self, key: &str) -> Result<Option<String>> {
119        self.parse_script_with(|script| {
120            script.sections().iter().find_map(|section| {
121                if let Section::ScriptInfo(info) = section {
122                    info.fields
123                        .iter()
124                        .find(|(k, _)| *k == key)
125                        .map(|(_, v)| v.to_string())
126                } else {
127                    None
128                }
129            })
130        })
131    }
132
133    /// Set script info field (ASS-aware editing)
134    pub fn set_script_info_field(&mut self, key: &str, value: &str) -> Result<()> {
135        // Simplified implementation - find and replace the field
136        let content = self.text();
137        let field_pattern = format!("{key}:");
138
139        if let Some(pos) = content.find(&field_pattern) {
140            // Find end of line
141            let line_start = pos;
142            let line_end = content[pos..].find('\n').map_or(content.len(), |n| pos + n);
143
144            let range = Range::new(Position::new(line_start), Position::new(line_end));
145
146            let new_line = format!("{key}: {value}");
147            self.replace(range, &new_line)?;
148        }
149
150        Ok(())
151    }
152}