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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
//! Bulk and transactional editing operations.
//!
//! Implements batched line updates, batched style/event insertion, and the
//! [`Script::atomic_batch_update`] driver that validates every operation before
//! applying changes to a cloned script for all-or-nothing semantics.
use alloc::vec::Vec;
use crate::parser::ast::Section;
use crate::parser::errors::ParseError;
use super::types::{BatchUpdateResult, EventBatch, StyleBatch, UpdateOperation};
use super::Script;
impl<'a> Script<'a> {
/// Perform multiple line updates in a single operation
///
/// Updates are performed in the order provided. If an update fails,
/// it's recorded in the failed list but doesn't stop other updates.
///
/// # Arguments
///
/// * `operations` - List of update operations to perform
///
/// # Returns
///
/// Result containing successful updates and failures
pub fn batch_update_lines(
&mut self,
operations: Vec<UpdateOperation<'a>>,
) -> BatchUpdateResult<'a> {
let mut result = BatchUpdateResult {
updated: Vec::with_capacity(operations.len()),
failed: Vec::new(),
};
// Sort operations by offset to process in order
let mut sorted_ops = operations;
sorted_ops.sort_by_key(|op| op.offset);
for op in sorted_ops {
match self.update_line_at_offset(op.offset, op.new_line, op.line_number) {
Ok(old_content) => {
result.updated.push((op.offset, old_content));
}
Err(e) => {
result.failed.push((op.offset, e));
}
}
}
result
}
/// Add multiple styles in a single operation
///
/// Creates the styles section if it doesn't exist.
///
/// # Arguments
///
/// * `batch` - Batch of styles to add
///
/// # Returns
///
/// Indices of the added styles within the styles section
pub fn batch_add_styles(&mut self, batch: StyleBatch<'a>) -> Vec<usize> {
let mut indices = Vec::with_capacity(batch.styles.len());
// Find or create styles section
let styles_section_index = self
.sections
.iter()
.position(|s| matches!(s, Section::Styles(_)));
if let Some(index) = styles_section_index {
if let Section::Styles(styles) = &mut self.sections[index] {
let start_index = styles.len();
styles.extend(batch.styles);
indices.extend(start_index..styles.len());
}
} else {
// Create new styles section
let count = batch.styles.len();
self.sections.push(Section::Styles(batch.styles));
indices.extend(0..count);
}
indices
}
/// Add multiple events in a single operation
///
/// Creates the events section if it doesn't exist.
///
/// # Arguments
///
/// * `batch` - Batch of events to add
///
/// # Returns
///
/// Indices of the added events within the events section
pub fn batch_add_events(&mut self, batch: EventBatch<'a>) -> Vec<usize> {
let mut indices = Vec::with_capacity(batch.events.len());
// Find or create events section
let events_section_index = self
.sections
.iter()
.position(|s| matches!(s, Section::Events(_)));
if let Some(index) = events_section_index {
if let Section::Events(events) = &mut self.sections[index] {
let start_index = events.len();
events.extend(batch.events);
indices.extend(start_index..events.len());
}
} else {
// Create new events section
let count = batch.events.len();
self.sections.push(Section::Events(batch.events));
indices.extend(0..count);
}
indices
}
/// Apply a batch of mixed operations atomically
///
/// All operations are validated first. If any validation fails,
/// no changes are made. This provides transactional semantics.
///
/// # Arguments
///
/// * `updates` - Line updates to perform
/// * `style_additions` - Styles to add
/// * `event_additions` - Events to add
///
/// # Returns
///
/// Ok if all operations succeed, Err with the first validation error
///
/// # Errors
///
/// Returns error if any operation would fail, without making changes
pub fn atomic_batch_update(
&mut self,
updates: Vec<UpdateOperation<'a>>,
style_additions: Option<StyleBatch<'a>>,
event_additions: Option<EventBatch<'a>>,
) -> core::result::Result<(), ParseError> {
// First, validate all updates
for op in &updates {
// Check if offset is valid
let section_found = self.sections.iter().any(|s| {
s.span()
.is_some_and(|span| span.start <= op.offset && op.offset < span.end)
});
if !section_found {
return Err(ParseError::SectionNotFound);
}
// Try parsing the line
self.parse_line_auto(op.new_line, op.line_number)?;
}
// All validations passed, now apply changes
// Clone self to preserve original state in case of failure
let mut temp_script = self.clone();
// Apply updates
for op in updates {
temp_script.update_line_at_offset(op.offset, op.new_line, op.line_number)?;
}
// Apply style additions
if let Some(styles) = style_additions {
temp_script.batch_add_styles(styles);
}
// Apply event additions
if let Some(events) = event_additions {
temp_script.batch_add_events(events);
}
// All operations succeeded, commit changes
*self = temp_script;
Ok(())
}
}