Skip to main content

ass_editor/commands/style_commands/
create.rs

1//! Style creation command for ASS documents.
2//!
3//! Provides [`CreateStyleCommand`] to insert a new style into the styles
4//! section, creating the section when it does not yet exist.
5
6use crate::commands::{CommandResult, EditorCommand};
7use crate::core::{EditorDocument, Position, Range, Result, StyleBuilder};
8
9#[cfg(not(feature = "std"))]
10use alloc::{format, string::String};
11
12/// Command to create a new style
13#[derive(Debug, Clone)]
14pub struct CreateStyleCommand {
15    pub style_name: String,
16    pub style_builder: StyleBuilder,
17    pub description: Option<String>,
18}
19
20impl CreateStyleCommand {
21    /// Create a new style creation command
22    pub fn new(style_name: String, style_builder: StyleBuilder) -> Self {
23        Self {
24            style_name,
25            style_builder,
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 CreateStyleCommand {
39    fn execute(&self, document: &mut EditorDocument) -> Result<CommandResult> {
40        // Build the style line with the provided name
41        let mut builder = self.style_builder.clone();
42        builder = builder.name(&self.style_name);
43        let style_line = builder.build()?;
44
45        // Find the styles section or create one
46        let content = document.text();
47        let styles_section_pos = content
48            .find("[V4+ Styles]")
49            .or_else(|| content.find("[V4 Styles]"))
50            .or_else(|| content.find("[Styles]"));
51
52        if let Some(section_start) = styles_section_pos {
53            // Find the end of the format line to insert after it
54            let section_content = &content[section_start..];
55            if let Some(format_line_end) = section_content.find('\n') {
56                let format_end_pos = section_start + format_line_end + 1;
57
58                // Find next format line or end of section
59                let insert_pos = if let Some(next_line_start) = content[format_end_pos..].find('\n')
60                {
61                    format_end_pos + next_line_start + 1
62                } else {
63                    format_end_pos
64                };
65
66                // Insert the new style
67                let insert_text = format!("{style_line}\n");
68                document.insert(Position::new(insert_pos), &insert_text)?;
69
70                let end_pos = Position::new(insert_pos + insert_text.len());
71                return Ok(CommandResult::success_with_change(
72                    Range::new(Position::new(insert_pos), end_pos),
73                    end_pos,
74                )
75                .with_message(format!("Created style '{}'", self.style_name)));
76            }
77        }
78
79        // No styles section found, create one
80        let styles_section = format!(
81            "\n[V4+ Styles]\nFormat: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n{style_line}\n"
82        );
83
84        // Insert before [Events] section if it exists, otherwise at the end
85        let insert_pos = if let Some(events_pos) = content.find("[Events]") {
86            events_pos
87        } else {
88            content.len()
89        };
90
91        document.insert(Position::new(insert_pos), &styles_section)?;
92
93        let end_pos = Position::new(insert_pos + styles_section.len());
94        Ok(CommandResult::success_with_change(
95            Range::new(Position::new(insert_pos), end_pos),
96            end_pos,
97        )
98        .with_message(format!(
99            "Created styles section and style '{}'",
100            self.style_name
101        )))
102    }
103
104    fn description(&self) -> &str {
105        self.description.as_deref().unwrap_or("Create style")
106    }
107
108    fn memory_usage(&self) -> usize {
109        core::mem::size_of::<Self>()
110            + self.style_name.len()
111            + self.description.as_ref().map_or(0, |d| d.len())
112            + 200 // Estimated StyleBuilder size
113    }
114}