Skip to main content

ass_editor/commands/
batch_command.rs

1//! Batch command that runs multiple commands as a single atomic operation.
2
3use crate::core::{EditorDocument, Position, Range, Result};
4
5use super::{CommandResult, EditorCommand};
6
7#[cfg(not(feature = "std"))]
8use alloc::{boxed::Box, format, string::String, vec::Vec};
9
10/// Batch command that executes multiple commands as a single atomic operation
11#[derive(Debug)]
12pub struct BatchCommand {
13    /// Commands to execute in order
14    pub commands: Vec<Box<dyn EditorCommand>>,
15    /// Description of the batch operation
16    pub description: String,
17}
18
19impl BatchCommand {
20    /// Create a new batch command
21    ///
22    /// # Examples
23    ///
24    /// ```
25    /// use ass_editor::{BatchCommand, InsertTextCommand, DeleteTextCommand, Position, Range, EditorDocument, EditorCommand};
26    ///
27    /// let mut doc = EditorDocument::from_content("Hello World").unwrap();
28    ///
29    /// let batch = BatchCommand::new("Multiple operations".to_string())
30    ///     .add_command(Box::new(InsertTextCommand::new(Position::new(5), " beautiful".to_string())))
31    ///     .add_command(Box::new(DeleteTextCommand::new(Range::new(Position::new(15), Position::new(21)))));
32    ///
33    /// let result = batch.execute(&mut doc).unwrap();
34    /// assert!(result.success);
35    /// assert_eq!(doc.text(), "Hello beautiful");
36    /// ```
37    pub fn new(description: String) -> Self {
38        Self {
39            commands: Vec::new(),
40            description,
41        }
42    }
43
44    /// Add a command to the batch
45    pub fn add_command(mut self, command: Box<dyn EditorCommand>) -> Self {
46        self.commands.push(command);
47        self
48    }
49
50    /// Add multiple commands to the batch
51    pub fn add_commands(mut self, commands: Vec<Box<dyn EditorCommand>>) -> Self {
52        self.commands.extend(commands);
53        self
54    }
55}
56
57impl EditorCommand for BatchCommand {
58    fn execute(&self, document: &mut EditorDocument) -> Result<CommandResult> {
59        let mut overall_result = CommandResult::success();
60        let mut first_range: Option<Range> = None;
61        let mut last_cursor: Option<Position> = None;
62
63        for command in &self.commands {
64            let result = command.execute(document)?;
65
66            if !result.success {
67                return Ok(CommandResult::failure(format!(
68                    "Batch command failed at: {}",
69                    command.description()
70                )));
71            }
72
73            // Track the overall range of changes
74            if let Some(range) = result.modified_range {
75                first_range = Some(match first_range {
76                    Some(existing) => existing.union(&range),
77                    None => range,
78                });
79            }
80
81            if result.new_cursor.is_some() {
82                last_cursor = result.new_cursor;
83            }
84
85            if result.content_changed {
86                overall_result.content_changed = true;
87            }
88        }
89
90        overall_result.modified_range = first_range;
91        overall_result.new_cursor = last_cursor;
92
93        Ok(overall_result)
94    }
95
96    fn description(&self) -> &str {
97        &self.description
98    }
99
100    fn memory_usage(&self) -> usize {
101        core::mem::size_of::<Self>()
102            + self.description.len()
103            + self
104                .commands
105                .iter()
106                .map(|c| c.memory_usage())
107                .sum::<usize>()
108    }
109}