Skip to main content

ass_editor/commands/event_commands/
effect_exec.rs

1//! `EditorCommand` implementation for `EventEffectCommand`.
2
3use super::effect::{EffectOperation, EventEffectCommand};
4use super::helpers::{collect_event_lines, parse_event_line};
5use crate::commands::{CommandResult, EditorCommand};
6use crate::core::{EditorDocument, Position, Range, Result};
7use ass_core::parser::ast::EventType;
8
9#[cfg(not(feature = "std"))]
10use alloc::{
11    format,
12    string::{String, ToString},
13    vec::Vec,
14};
15
16impl EditorCommand for EventEffectCommand {
17    fn execute(&self, document: &mut EditorDocument) -> Result<CommandResult> {
18        let content = document.text();
19        let event_lines = collect_event_lines(&content)?;
20        let mut replacements = Vec::new();
21        let mut total_range: Option<Range> = None;
22
23        for event_line in event_lines {
24            let should_modify =
25                self.event_indices.is_empty() || self.event_indices.contains(&event_line.index);
26
27            if !should_modify {
28                continue;
29            }
30
31            if let Ok(event) = parse_event_line(event_line.line) {
32                let new_effect = match self.operation {
33                    EffectOperation::Set => self.effect.clone(),
34                    EffectOperation::Clear => String::new(),
35                    EffectOperation::Append => {
36                        if event.effect.is_empty() {
37                            self.effect.clone()
38                        } else {
39                            format!("{} {}", event.effect, self.effect)
40                        }
41                    }
42                    EffectOperation::Prepend => {
43                        if event.effect.is_empty() {
44                            self.effect.clone()
45                        } else {
46                            format!("{} {}", self.effect, event.effect)
47                        }
48                    }
49                };
50
51                let event_type_str = match event.event_type {
52                    EventType::Dialogue => "Dialogue",
53                    EventType::Comment => "Comment",
54                    _ => "Dialogue",
55                };
56                let new_line = format!(
57                    "{}: {},{},{},{},{},{},{},{},{},{}",
58                    event_type_str,
59                    event.layer,
60                    event.start,
61                    event.end,
62                    event.style,
63                    event.name,
64                    event.margin_l,
65                    event.margin_r,
66                    event.margin_v,
67                    new_effect,
68                    event.text
69                );
70
71                let change_range = Range::new(
72                    Position::new(event_line.start),
73                    Position::new(event_line.start + new_line.len()),
74                );
75                total_range = Some(match total_range {
76                    Some(existing) => existing.union(&change_range),
77                    None => change_range,
78                });
79                replacements.push((event_line.start, event_line.end, new_line));
80            }
81        }
82
83        for (start, end, new_line) in replacements.iter().rev() {
84            let range = Range::new(Position::new(*start), Position::new(*end));
85            document.replace(range, new_line)?;
86        }
87
88        let changes_made = replacements.len();
89
90        if changes_made > 0 {
91            let operation_name = match self.operation {
92                EffectOperation::Set => "set",
93                EffectOperation::Clear => "cleared",
94                EffectOperation::Append => "appended",
95                EffectOperation::Prepend => "prepended",
96            };
97
98            Ok(CommandResult::success_with_change(
99                total_range.unwrap_or(Range::new(Position::new(0), Position::new(0))),
100                Position::new(document.len_bytes()),
101            )
102            .with_message(format!("Effect {operation_name} for {changes_made} events")))
103        } else {
104            Ok(CommandResult::success().with_message("No events were modified".to_string()))
105        }
106    }
107
108    fn description(&self) -> &str {
109        self.description.as_deref().unwrap_or("Modify event effect")
110    }
111
112    fn memory_usage(&self) -> usize {
113        core::mem::size_of::<Self>()
114            + self.event_indices.len() * core::mem::size_of::<usize>()
115            + self.effect.len()
116            + self.description.as_ref().map_or(0, |d| d.len())
117    }
118}