ass_editor/commands/style_commands/
apply.rs1use crate::commands::{CommandResult, EditorCommand};
7use crate::core::{EditorDocument, EditorError, Position, Range, Result};
8
9#[cfg(not(feature = "std"))]
10use alloc::{
11 format,
12 string::{String, ToString},
13 vec::Vec,
14};
15
16#[derive(Debug, Clone)]
18pub struct ApplyStyleCommand {
19 pub old_style: String,
20 pub new_style: String,
21 pub event_filter: Option<String>, pub description: Option<String>,
23}
24
25impl ApplyStyleCommand {
26 pub fn new(old_style: String, new_style: String) -> Self {
28 Self {
29 old_style,
30 new_style,
31 event_filter: None,
32 description: None,
33 }
34 }
35
36 pub fn with_filter(mut self, filter: String) -> Self {
38 self.event_filter = Some(filter);
39 self
40 }
41
42 #[must_use]
44 pub fn with_description(mut self, description: String) -> Self {
45 self.description = Some(description);
46 self
47 }
48}
49
50impl EditorCommand for ApplyStyleCommand {
51 fn execute(&self, document: &mut EditorDocument) -> Result<CommandResult> {
52 let mut content = document.text();
53 let mut changes_made = 0;
54 let mut total_range: Option<Range> = None;
55
56 let events_start = content
58 .find("[Events]")
59 .ok_or_else(|| EditorError::command_failed("Events section not found"))?;
60
61 let events_content_start = content[events_start..]
63 .find('\n')
64 .map(|pos| events_start + pos + 1)
65 .ok_or_else(|| EditorError::command_failed("Invalid events section format"))?;
66
67 let first_event_start = content[events_content_start..]
69 .find('\n')
70 .map(|pos| events_content_start + pos + 1)
71 .unwrap_or(events_content_start);
72
73 let mut search_pos = first_event_start;
74
75 while search_pos < content.len() {
76 let line_start = search_pos;
78 let line_end = content[line_start..]
79 .find('\n')
80 .map(|pos| line_start + pos)
81 .unwrap_or(content.len());
82
83 if line_start >= line_end {
84 break;
85 }
86
87 let line = &content[line_start..line_end];
88
89 if line.starts_with("Dialogue:") || line.starts_with("Comment:") {
91 let parts: Vec<&str> = line.split(',').collect();
92
93 if parts.len() > 4 && parts[3].trim() == self.old_style {
95 let should_update = if let Some(ref filter) = self.event_filter {
97 line.contains(filter)
98 } else {
99 true
100 };
101
102 if should_update {
103 let updated_line = line.replace(
105 &format!(",{},", self.old_style),
106 &format!(",{},", self.new_style),
107 );
108
109 let range = Range::new(Position::new(line_start), Position::new(line_end));
111 document.replace(range, &updated_line)?;
112
113 content = document.text();
115
116 let change_range = Range::new(
118 Position::new(line_start),
119 Position::new(line_start + updated_line.len()),
120 );
121 total_range = Some(match total_range {
122 Some(existing) => existing.union(&change_range),
123 None => change_range,
124 });
125
126 changes_made += 1;
127 }
128 }
129 } else if line.starts_with('[') && line != "[Events]" {
130 break;
132 }
133
134 search_pos = line_end + 1;
135 }
136
137 if changes_made > 0 {
138 Ok(CommandResult::success_with_change(
139 total_range.unwrap_or(Range::new(Position::new(0), Position::new(0))),
140 Position::new(content.len()),
141 )
142 .with_message(format!(
143 "Applied style '{}' to {} events",
144 self.new_style, changes_made
145 )))
146 } else {
147 Ok(CommandResult::success()
148 .with_message("No events found matching the criteria".to_string()))
149 }
150 }
151
152 fn description(&self) -> &str {
153 self.description
154 .as_deref()
155 .unwrap_or("Apply style to events")
156 }
157
158 fn memory_usage(&self) -> usize {
159 core::mem::size_of::<Self>()
160 + self.old_style.len()
161 + self.new_style.len()
162 + self.event_filter.as_ref().map_or(0, |f| f.len())
163 + self.description.as_ref().map_or(0, |d| d.len())
164 }
165}