Skip to main content

ass_editor/core/builders/
event_build.rs

1//! Event line serialization for [`EventBuilder`].
2
3use super::EventBuilder;
4use crate::core::errors::{EditorError, Result};
5use ass_core::parser::ast::EventType;
6use ass_core::ScriptVersion;
7
8#[cfg(feature = "std")]
9use std::borrow::Cow;
10
11#[cfg(not(feature = "std"))]
12use alloc::{
13    borrow::Cow,
14    format,
15    string::{String, ToString},
16    vec::Vec,
17};
18
19impl EventBuilder<'_> {
20    /// Build the event (validates required fields)
21    pub fn build(self) -> Result<String> {
22        // Default to v4+ format
23        self.build_with_version(ScriptVersion::AssV4)
24    }
25
26    /// Build the event with a specific format version
27    pub fn build_with_version(self, version: ScriptVersion) -> Result<String> {
28        let event_type = self.event_type.unwrap_or(EventType::Dialogue);
29        let start = self.start.unwrap_or(Cow::Borrowed("0:00:00.00"));
30        let end = self.end.unwrap_or(Cow::Borrowed("0:00:05.00"));
31        let style = self.style.unwrap_or(Cow::Borrowed("Default"));
32        let name = self.name.unwrap_or(Cow::Borrowed(""));
33        let text = self.text.unwrap_or(Cow::Borrowed(""));
34        let layer = self.layer.unwrap_or(Cow::Borrowed("0"));
35        let margin_l = self.margin_l.unwrap_or(Cow::Borrowed("0"));
36        let margin_r = self.margin_r.unwrap_or(Cow::Borrowed("0"));
37        let margin_v = self.margin_v.unwrap_or(Cow::Borrowed("0"));
38        let effect = self.effect.unwrap_or(Cow::Borrowed(""));
39
40        // Format as ASS event line based on version
41        let event_type_str = event_type.as_str();
42        let line = match version {
43            ScriptVersion::SsaV4 => {
44                // SSA v4 format: no layer field, uses Marked=0 prefix
45                format!(
46                    "{event_type_str}: Marked=0,{start},{end},{style},{name},{margin_l},{margin_r},{margin_v},{effect},{text}"
47                )
48            }
49            ScriptVersion::AssV4 => {
50                // ASS v4 format: includes layer field
51                format!(
52                    "{event_type_str}: {layer},{start},{end},{style},{name},{margin_l},{margin_r},{margin_v},{effect},{text}"
53                )
54            }
55            ScriptVersion::AssV4Plus => {
56                // ASS v4++ format: can use margin_t/margin_b if specified
57                // For now, we use the same format as v4 since the builder doesn't support margin_t/margin_b yet
58                format!(
59                    "{event_type_str}: {layer},{start},{end},{style},{name},{margin_l},{margin_r},{margin_v},{effect},{text}"
60                )
61            }
62        };
63
64        Ok(line)
65    }
66
67    /// Build the event with a specific format line
68    /// The format parameter should contain field names like ["Layer", "Start", "End", "Style", "Text"]
69    pub fn build_with_format(&self, format: &[&str]) -> Result<String> {
70        if format.is_empty() {
71            return Err(EditorError::FormatLineError {
72                message: "Format line cannot be empty".to_string(),
73            });
74        }
75
76        let event_type = self.event_type.unwrap_or(EventType::Dialogue);
77        let event_type_str = event_type.as_str();
78
79        // Build field values based on format specification
80        let mut field_values = Vec::with_capacity(format.len());
81
82        for field in format {
83            let value = match *field {
84                "Layer" => self.layer.as_ref().map(|c| c.as_ref()).unwrap_or("0"),
85                "Start" => self
86                    .start
87                    .as_ref()
88                    .map(|c| c.as_ref())
89                    .unwrap_or("0:00:00.00"),
90                "End" => self
91                    .end
92                    .as_ref()
93                    .map(|c| c.as_ref())
94                    .unwrap_or("0:00:05.00"),
95                "Style" => self.style.as_ref().map(|c| c.as_ref()).unwrap_or("Default"),
96                "Name" | "Actor" => self.name.as_ref().map(|c| c.as_ref()).unwrap_or(""),
97                "MarginL" => self.margin_l.as_ref().map(|c| c.as_ref()).unwrap_or("0"),
98                "MarginR" => self.margin_r.as_ref().map(|c| c.as_ref()).unwrap_or("0"),
99                "MarginV" => self.margin_v.as_ref().map(|c| c.as_ref()).unwrap_or("0"),
100                "MarginT" => self.margin_t.as_ref().map(|c| c.as_ref()).unwrap_or("0"),
101                "MarginB" => self.margin_b.as_ref().map(|c| c.as_ref()).unwrap_or("0"),
102                "Effect" => self.effect.as_ref().map(|c| c.as_ref()).unwrap_or(""),
103                "Text" => self.text.as_ref().map(|c| c.as_ref()).unwrap_or(""),
104                _ => {
105                    return Err(EditorError::FormatLineError {
106                        message: format!("Unknown event field: {field}"),
107                    })
108                }
109            };
110            field_values.push(value.to_string());
111        }
112
113        // Build the event line
114        let line = format!("{event_type_str}: {}", field_values.join(","));
115        Ok(line)
116    }
117}