Skip to main content

ass_editor/commands/karaoke_commands/
split.rs

1//! Split existing karaoke timing into separate timed segments.
2
3use crate::commands::{CommandResult, EditorCommand};
4use crate::core::{EditorDocument, Position, Range, Result};
5
6#[cfg(not(feature = "std"))]
7use alloc::{format, string::String, vec::Vec};
8
9/// Split karaoke timing at specific points
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct SplitKaraokeCommand {
12    /// Range containing karaoke to split
13    pub range: Range,
14    /// Character positions to split at (relative to range start)
15    pub split_positions: Vec<usize>,
16    /// New duration for each split segment
17    pub new_duration: Option<u32>,
18}
19
20impl SplitKaraokeCommand {
21    /// Create a new split karaoke command
22    pub fn new(range: Range, split_positions: Vec<usize>) -> Self {
23        Self {
24            range,
25            split_positions,
26            new_duration: None,
27        }
28    }
29
30    /// Set new duration for split segments
31    #[must_use]
32    pub fn duration(mut self, duration: u32) -> Self {
33        self.new_duration = Some(duration);
34        self
35    }
36}
37
38impl EditorCommand for SplitKaraokeCommand {
39    fn execute(&self, document: &mut EditorDocument) -> Result<CommandResult> {
40        let original_text = document.text_range(self.range)?;
41        let processed_text = self.split_karaoke_text(&original_text)?;
42
43        document.replace_raw(self.range, &processed_text)?;
44
45        let end_pos = Position::new(self.range.start.offset + processed_text.len());
46        let range = Range::new(self.range.start, end_pos);
47
48        Ok(CommandResult::success_with_change(range, end_pos))
49    }
50
51    fn description(&self) -> &str {
52        "Split karaoke timing"
53    }
54
55    fn memory_usage(&self) -> usize {
56        core::mem::size_of::<Self>() + self.split_positions.len() * core::mem::size_of::<usize>()
57    }
58}
59
60impl SplitKaraokeCommand {
61    /// Split karaoke text at specified positions
62    fn split_karaoke_text(&self, text: &str) -> Result<String> {
63        // For now, return simplified version
64        // In practice, this would parse existing karaoke tags and split them
65        let mut result = String::new();
66        let mut last_pos = 0;
67
68        for &pos in &self.split_positions {
69            if pos <= last_pos || pos >= text.len() {
70                continue;
71            }
72
73            let segment = &text[last_pos..pos];
74            if !segment.is_empty() {
75                let duration = self.new_duration.unwrap_or(50);
76                result.push_str(&format!("{{\\k{duration}}}{segment}"));
77            }
78            last_pos = pos;
79        }
80
81        // Add remaining text
82        if last_pos < text.len() {
83            let segment = &text[last_pos..];
84            if !segment.is_empty() {
85                let duration = self.new_duration.unwrap_or(50);
86                result.push_str(&format!("{{\\k{duration}}}{segment}"));
87            }
88        }
89
90        Ok(result)
91    }
92}