Skip to main content

ass_editor/commands/script_info_commands/
set.rs

1//! Command to set or update a property in the `[Script Info]` section.
2
3use crate::commands::{CommandResult, EditorCommand};
4use crate::core::{EditorDocument, EditorError, Position, Range, Result};
5
6#[cfg(not(feature = "std"))]
7use alloc::{
8    format,
9    string::{String, ToString},
10};
11
12/// Command to set a script info property in the ASS document
13///
14/// Sets or updates properties like Title, Author, PlayResX, PlayResY, etc.
15/// in the `[Script Info]` section. Creates the section if it doesn't exist.
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct SetScriptInfoCommand {
18    /// Property name (e.g., "Title", "Author", "PlayResX")
19    pub property: String,
20    /// New value for the property
21    pub value: String,
22}
23
24impl SetScriptInfoCommand {
25    /// Create a new set script info command
26    pub fn new(property: String, value: String) -> Self {
27        Self { property, value }
28    }
29}
30
31impl EditorCommand for SetScriptInfoCommand {
32    fn execute(&self, document: &mut EditorDocument) -> Result<CommandResult> {
33        let content = document.text().to_string();
34
35        // Find [Script Info] section
36        let script_info_start = content
37            .find("[Script Info]")
38            .ok_or_else(|| EditorError::command_failed("No [Script Info] section found"))?;
39
40        // Find the end of Script Info section (next section or end of file)
41        let script_info_end = content[script_info_start..]
42            .find("\n[")
43            .map(|pos| script_info_start + pos)
44            .unwrap_or(content.len());
45
46        // Check if property already exists
47        let property_pattern = format!("{}: ", self.property);
48        let search_range = &content[script_info_start..script_info_end];
49
50        if let Some(prop_pos) = search_range.find(&property_pattern) {
51            // Property exists, update it
52            let absolute_pos = script_info_start + prop_pos;
53            let line_start = absolute_pos;
54            let line_end = content[absolute_pos..]
55                .find('\n')
56                .map(|pos| absolute_pos + pos)
57                .unwrap_or(content.len());
58
59            let new_line = format!("{}: {}", self.property, self.value);
60            let range = Range::new(Position::new(line_start), Position::new(line_end));
61
62            document.replace(range, &new_line)?;
63
64            Ok(CommandResult::success_with_change(
65                Range::new(
66                    Position::new(line_start),
67                    Position::new(line_start + new_line.len()),
68                ),
69                Position::new(line_start + new_line.len()),
70            ))
71        } else {
72            // Property doesn't exist, add it after [Script Info]
73            let insert_pos = content[script_info_start..]
74                .find('\n')
75                .map(|pos| script_info_start + pos + 1)
76                .unwrap_or(script_info_start + 13); // Length of "[Script Info]"
77
78            let new_line = format!("{}: {}\n", self.property, self.value);
79            document.insert_raw(Position::new(insert_pos), &new_line)?;
80
81            Ok(CommandResult::success_with_change(
82                Range::new(
83                    Position::new(insert_pos),
84                    Position::new(insert_pos + new_line.len()),
85                ),
86                Position::new(insert_pos + new_line.len()),
87            ))
88        }
89    }
90
91    fn description(&self) -> &str {
92        "Set script info property"
93    }
94
95    fn memory_usage(&self) -> usize {
96        core::mem::size_of::<Self>() + self.property.len() + self.value.len()
97    }
98}