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}