Skip to main content

ass_editor/core/document/
event_edit.rs

1//! Index-based structured event editing
2//!
3//! Implements `edit_event_by_index`, which locates an event in the raw text,
4//! applies field-level modifications, and rewrites the dialogue/comment line
5//! via the `build_modified_event_line_from_data` helper.
6
7use super::EditorDocument;
8use crate::core::errors::{EditorError, 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    /// Edit event by index with full field support
21    ///
22    /// Allows structured editing of specific event fields by index.
23    /// Returns the modified event line for undo support.
24    ///
25    /// # Arguments
26    ///
27    /// * `index` - Zero-based index of the event to edit
28    /// * `update_fn` - Function that receives the current event and returns modifications
29    ///
30    /// # Example
31    ///
32    /// ```rust
33    /// # use ass_editor::core::EditorDocument;
34    /// # let content = r#"[Script Info]
35    /// # Title: Test
36    /// #
37    /// # [Events]
38    /// # Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
39    /// # Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,,Original text"#;
40    /// # let mut doc = EditorDocument::from_content(content).unwrap();
41    /// doc.edit_event_by_index(0, |event| {
42    ///     vec![
43    ///         ("text", "New dialogue text".to_string()),
44    ///         ("style", "NewStyle".to_string()),
45    ///     ]
46    /// })?;
47    /// # Ok::<(), Box<dyn std::error::Error>>(())
48    /// ```
49    pub fn edit_event_by_index<F>(&mut self, index: usize, update_fn: F) -> Result<String>
50    where
51        F: for<'a> FnOnce(&ass_core::parser::ast::Event<'a>) -> Vec<(&'static str, String)>,
52    {
53        let content = self.text();
54        let mut event_info = None;
55        let mut event_count = 0;
56
57        // Find the event and its location in the document
58        self.parse_script_with(|script| -> Result<()> {
59            for section in script.sections() {
60                if let Section::Events(events) = section {
61                    for event in events {
62                        if event_count == index {
63                            // Get the modifications from the update function
64                            let modifications = update_fn(event);
65
66                            // Build a pattern to search for this specific event
67                            let event_type_str = event.event_type.as_str();
68                            let pattern = format!(
69                                "{}: {},{},{}",
70                                event_type_str, event.layer, event.start, event.end
71                            );
72
73                            let event_line = if let Some(pos) = content.find(&pattern) {
74                                let line_end = content[pos..]
75                                    .find('\n')
76                                    .map(|n| pos + n)
77                                    .unwrap_or(content.len());
78                                let line = content[pos..line_end].to_string();
79                                (pos, line_end, line)
80                            } else {
81                                return Err(EditorError::ValidationError {
82                                    message: "Could not find event line in document".to_string(),
83                                });
84                            };
85
86                            // Store the event data we need instead of cloning
87                            let event_data = (
88                                event.event_type,
89                                event.layer.to_string(),
90                                event.start.to_string(),
91                                event.end.to_string(),
92                                event.style.to_string(),
93                                event.name.to_string(),
94                                event.margin_l.to_string(),
95                                event.margin_r.to_string(),
96                                event.margin_v.to_string(),
97                                event.effect.to_string(),
98                                event.text.to_string(),
99                            );
100                            event_info = Some((event_data, event_line, modifications));
101                            return Ok(());
102                        }
103                        event_count += 1;
104                    }
105                }
106            }
107            Ok(())
108        })??;
109
110        if let Some((event_data, (line_start, line_end, original_line), modifications)) = event_info
111        {
112            // Build the new event line with modifications
113            let new_line = self.build_modified_event_line_from_data(
114                event_data,
115                &original_line,
116                modifications,
117            )?;
118
119            // Replace the line in the document
120            let range = Range::new(Position::new(line_start), Position::new(line_end));
121            self.replace(range, &new_line)?;
122
123            Ok(new_line)
124        } else {
125            Err(EditorError::InvalidRange {
126                start: index,
127                end: index + 1,
128                length: event_count,
129            })
130        }
131    }
132}