1use crate::core::{EditorDocument, Position, Range, Result};
6
7#[cfg(not(feature = "std"))]
8use alloc::{boxed::Box, format, string::String, vec::Vec};
9
10pub trait DeltaCommand {
12 fn execute_with_delta(&self, document: &mut EditorDocument) -> Result<super::CommandResult>;
14
15 fn description(&self) -> String;
17
18 fn supports_incremental(&self) -> bool {
20 cfg!(feature = "stream")
21 }
22}
23
24#[derive(Debug, Clone)]
26pub struct IncrementalInsertCommand {
27 pub position: Position,
28 pub text: String,
29}
30
31impl IncrementalInsertCommand {
32 pub fn new(position: Position, text: String) -> Self {
34 Self { position, text }
35 }
36}
37
38impl DeltaCommand for IncrementalInsertCommand {
39 fn execute_with_delta(&self, document: &mut EditorDocument) -> Result<super::CommandResult> {
40 #[cfg(feature = "stream")]
41 {
42 if self.supports_incremental() {
43 match document.insert_incremental(self.position, &self.text) {
45 Ok(delta) => {
46 let result = super::CommandResult {
47 success: true,
48 message: Some(format!(
49 "Inserted text at position {}",
50 self.position.offset
51 )),
52 modified_range: Some(Range::new(
53 self.position,
54 Position::new(self.position.offset + self.text.len()),
55 )),
56 new_cursor: Some(Position::new(self.position.offset + self.text.len())),
57 content_changed: true,
58 script_delta: Some(delta),
59 };
60 return Ok(result);
61 }
62 Err(_) => {
63 }
65 }
66 }
67 }
68
69 document.insert(self.position, &self.text)?;
71 let result = super::CommandResult::success_with_change(
72 Range::new(
73 self.position,
74 Position::new(self.position.offset + self.text.len()),
75 ),
76 Position::new(self.position.offset + self.text.len()),
77 );
78 Ok(result)
79 }
80
81 fn description(&self) -> String {
82 format!(
83 "Insert '{}' at position {}",
84 self.text, self.position.offset
85 )
86 }
87}
88
89#[derive(Debug, Clone)]
91pub struct IncrementalReplaceCommand {
92 pub range: Range,
93 pub new_text: String,
94}
95
96impl IncrementalReplaceCommand {
97 pub fn new(range: Range, new_text: String) -> Self {
99 Self { range, new_text }
100 }
101}
102
103impl DeltaCommand for IncrementalReplaceCommand {
104 fn execute_with_delta(&self, document: &mut EditorDocument) -> Result<super::CommandResult> {
105 #[cfg(feature = "stream")]
106 {
107 if self.supports_incremental() {
108 match document.edit_incremental(self.range, &self.new_text) {
110 Ok(delta) => {
111 let result = super::CommandResult {
112 success: true,
113 message: Some(format!(
114 "Replaced text in range {}..{}",
115 self.range.start.offset, self.range.end.offset
116 )),
117 modified_range: Some(Range::new(
118 self.range.start,
119 Position::new(self.range.start.offset + self.new_text.len()),
120 )),
121 new_cursor: Some(Position::new(
122 self.range.start.offset + self.new_text.len(),
123 )),
124 content_changed: true,
125 script_delta: Some(delta),
126 };
127 return Ok(result);
128 }
129 Err(_) => {
130 }
132 }
133 }
134 }
135
136 document.replace(self.range, &self.new_text)?;
138 let result = super::CommandResult::success_with_change(
139 Range::new(
140 self.range.start,
141 Position::new(self.range.start.offset + self.new_text.len()),
142 ),
143 Position::new(self.range.start.offset + self.new_text.len()),
144 );
145 Ok(result)
146 }
147
148 fn description(&self) -> String {
149 format!(
150 "Replace text in range {}..{} with '{}'",
151 self.range.start.offset, self.range.end.offset, self.new_text
152 )
153 }
154}
155
156#[derive(Debug, Clone)]
158pub struct IncrementalEventEditCommand {
159 pub old_text: String,
160 pub new_text: String,
161}
162
163impl IncrementalEventEditCommand {
164 pub fn new(old_text: String, new_text: String) -> Self {
166 Self { old_text, new_text }
167 }
168}
169
170impl DeltaCommand for IncrementalEventEditCommand {
171 fn execute_with_delta(&self, document: &mut EditorDocument) -> Result<super::CommandResult> {
172 #[cfg(feature = "stream")]
173 {
174 if self.supports_incremental() {
175 match document.edit_event_incremental(&self.old_text, &self.new_text) {
177 Ok(delta) => {
178 let result = super::CommandResult {
179 success: true,
180 message: Some(format!(
181 "Edited event: '{}' → '{}'",
182 self.old_text, self.new_text
183 )),
184 modified_range: None, new_cursor: None,
186 content_changed: true,
187 script_delta: Some(delta),
188 };
189 return Ok(result);
190 }
191 Err(_) => {
192 }
194 }
195 }
196 }
197
198 document.edit_event_text(&self.old_text, &self.new_text)?;
200 let result = super::CommandResult {
201 success: true,
202 message: Some(format!(
203 "Edited event: '{}' → '{}'",
204 self.old_text, self.new_text
205 )),
206 modified_range: None,
207 new_cursor: None,
208 content_changed: true,
209 #[cfg(feature = "stream")]
210 script_delta: None,
211 };
212 Ok(result)
213 }
214
215 fn description(&self) -> String {
216 format!("Edit event: '{}' → '{}'", self.old_text, self.new_text)
217 }
218}
219
220pub struct DeltaBatchCommand {
222 commands: Vec<Box<dyn DeltaCommand>>,
223}
224
225impl DeltaBatchCommand {
226 pub fn new() -> Self {
228 Self {
229 commands: Vec::new(),
230 }
231 }
232
233 pub fn add_command<T: DeltaCommand + 'static>(mut self, command: T) -> Self {
235 self.commands.push(Box::new(command));
236 self
237 }
238
239 pub fn execute_batch(
241 &self,
242 document: &mut EditorDocument,
243 ) -> Result<Vec<super::CommandResult>> {
244 let mut results = Vec::new();
245
246 for command in &self.commands {
247 let result = command.execute_with_delta(document)?;
248 results.push(result);
249 }
250
251 Ok(results)
252 }
253}
254
255impl Default for DeltaBatchCommand {
256 fn default() -> Self {
257 Self::new()
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264 use crate::EditorDocument;
265 #[cfg(not(feature = "std"))]
266 use alloc::string::ToString;
267 #[cfg(not(feature = "std"))]
268 #[test]
269 fn test_incremental_insert_command() {
270 let mut doc = EditorDocument::from_content("[Script Info]\nTitle: Test").unwrap();
271
272 let command = IncrementalInsertCommand::new(
273 Position::new(doc.len_bytes()),
274 "\nAuthor: Test Author".to_string(),
275 );
276
277 let result = command.execute_with_delta(&mut doc).unwrap();
278 assert!(result.success);
279 assert!(result.content_changed);
280 assert!(doc.text().contains("Author: Test Author"));
281 }
282
283 #[test]
284 fn test_incremental_event_edit_command() {
285 let content = r#"[Events]
286Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
287Dialogue: 0,0:00:05.00,0:00:10.00,Default,John,0,0,0,,Hello, world!"#;
288
289 let mut doc = EditorDocument::from_content(content).unwrap();
290
291 let command = IncrementalEventEditCommand::new(
292 "Hello, world!".to_string(),
293 "Hello, ASS-RS!".to_string(),
294 );
295
296 let result = command.execute_with_delta(&mut doc).unwrap();
297 assert!(result.success);
298 assert!(doc.text().contains("Hello, ASS-RS!"));
299 }
300
301 #[test]
302 fn test_delta_batch_command() {
303 let mut doc = EditorDocument::from_content("[Script Info]\nTitle: Test").unwrap();
304
305 let batch = DeltaBatchCommand::new().add_command(IncrementalInsertCommand::new(
307 Position::new(doc.len_bytes()),
308 "\nAuthor: Test".to_string(),
309 ));
310
311 let results = batch.execute_batch(&mut doc).unwrap();
312 assert_eq!(results.len(), 1);
313 assert!(results.iter().all(|r| r.success));
314 assert!(doc.text().contains("Author: Test"));
315 }
316}