Skip to main content

ass_editor/commands/event_commands/
split.rs

1//! Command to split a single event into two at a specific time.
2
3use super::helpers::{collect_event_lines, parse_event_line};
4use crate::commands::{CommandResult, EditorCommand};
5use crate::core::{EditorDocument, EditorError, Position, Range, Result};
6use ass_core::parser::ast::EventType;
7use ass_core::utils::parse_ass_time;
8
9#[cfg(not(feature = "std"))]
10use alloc::{format, string::String};
11
12/// Command to split an event at a specific time
13#[derive(Debug, Clone)]
14pub struct SplitEventCommand {
15    pub event_index: usize,
16    pub split_time: String, // Time in ASS format (H:MM:SS.CC)
17    pub description: Option<String>,
18}
19
20impl SplitEventCommand {
21    /// Create a new event split command
22    pub fn new(event_index: usize, split_time: String) -> Self {
23        Self {
24            event_index,
25            split_time,
26            description: None,
27        }
28    }
29
30    /// Set a custom description for this command
31    #[must_use]
32    pub fn with_description(mut self, description: String) -> Self {
33        self.description = Some(description);
34        self
35    }
36}
37
38impl EditorCommand for SplitEventCommand {
39    fn execute(&self, document: &mut EditorDocument) -> Result<CommandResult> {
40        // Parse split time to validate it
41        let split_time_cs = parse_ass_time(&self.split_time).map_err(|_| {
42            EditorError::command_failed(format!("Invalid time format: {}", self.split_time))
43        })?;
44
45        let content = document.text();
46        let event_lines = collect_event_lines(&content)?;
47
48        if let Some(event_line) = event_lines
49            .into_iter()
50            .find(|event_line| event_line.index == self.event_index)
51        {
52            let event = parse_event_line(event_line.line)?;
53
54            // Validate split time is within event bounds
55            let start_time_cs = event
56                .start_time_cs()
57                .map_err(|_| EditorError::command_failed("Invalid start time in event"))?;
58            let end_time_cs = event
59                .end_time_cs()
60                .map_err(|_| EditorError::command_failed("Invalid end time in event"))?;
61
62            if split_time_cs <= start_time_cs || split_time_cs >= end_time_cs {
63                return Err(EditorError::command_failed(
64                    "Split time must be between event start and end times",
65                ));
66            }
67
68            // Create two new events
69            let event_type_str = match event.event_type {
70                EventType::Dialogue => "Dialogue",
71                EventType::Comment => "Comment",
72                _ => "Dialogue", // Default fallback
73            };
74            let first_event = format!(
75                "{}: {},{},{},{},{},{},{},{},{},{}",
76                event_type_str,
77                event.layer,
78                event.start,
79                self.split_time,
80                event.style,
81                event.name,
82                event.margin_l,
83                event.margin_r,
84                event.margin_v,
85                event.effect,
86                event.text
87            );
88
89            let second_event = format!(
90                "{}: {},{},{},{},{},{},{},{},{},{}",
91                event_type_str,
92                event.layer,
93                self.split_time,
94                event.end,
95                event.style,
96                event.name,
97                event.margin_l,
98                event.margin_r,
99                event.margin_v,
100                event.effect,
101                event.text
102            );
103
104            // Replace the original event with the two new events
105            let replacement = format!("{first_event}\n{second_event}");
106            let range = Range::new(
107                Position::new(event_line.start),
108                Position::new(event_line.end),
109            );
110            document.replace(range, &replacement)?;
111
112            let end_pos = Position::new(event_line.start + replacement.len());
113            return Ok(CommandResult::success_with_change(
114                Range::new(Position::new(event_line.start), end_pos),
115                end_pos,
116            )
117            .with_message(format!(
118                "Split event {} at time {}",
119                self.event_index, self.split_time
120            )));
121        }
122
123        Err(EditorError::command_failed(format!(
124            "Event index {} not found",
125            self.event_index
126        )))
127    }
128
129    fn description(&self) -> &str {
130        self.description.as_deref().unwrap_or("Split event")
131    }
132
133    fn memory_usage(&self) -> usize {
134        core::mem::size_of::<Self>()
135            + self.split_time.len()
136            + self.description.as_ref().map_or(0, |d| d.len())
137    }
138}