Skip to main content

ass_core/parser/ast/event/
serialization.rs

1//! ASS string serialization and span validation for [`Event`]
2//!
3//! Provides the default and format-driven `to_ass_string` conversions plus the
4//! debug-only `validate_spans` zero-copy invariant check.
5
6#[cfg(not(feature = "std"))]
7extern crate alloc;
8#[cfg(not(feature = "std"))]
9use alloc::{format, vec::Vec};
10
11use super::Event;
12#[cfg(debug_assertions)]
13use core::ops::Range;
14
15impl Event<'_> {
16    /// Convert event to ASS string representation
17    ///
18    /// Generates the standard ASS event line format. Uses `margin_v` by default,
19    /// but will use `margin_t/margin_b` if provided (V4++ format).
20    ///
21    /// # Examples
22    ///
23    /// ```rust
24    /// # use ass_core::parser::ast::{Event, EventType};
25    /// let event = Event {
26    ///     event_type: EventType::Dialogue,
27    ///     layer: "0",
28    ///     start: "0:00:05.00",
29    ///     end: "0:00:10.00",
30    ///     style: "Default",
31    ///     text: "Hello",
32    ///     ..Event::default()
33    /// };
34    /// assert_eq!(
35    ///     event.to_ass_string(),
36    ///     "Dialogue: 0,0:00:05.00,0:00:10.00,Default,,0,0,0,,Hello"
37    /// );
38    /// ```
39    #[must_use]
40    pub fn to_ass_string(&self) -> alloc::string::String {
41        let event_type_str = self.event_type.as_str();
42
43        // Use standard V4+ format by default
44        // TODO: Support custom format lines
45        format!(
46            "{event_type_str}: {},{},{},{},{},{},{},{},{},{}",
47            self.layer,
48            self.start,
49            self.end,
50            self.style,
51            self.name,
52            self.margin_l,
53            self.margin_r,
54            self.margin_v,
55            self.effect,
56            self.text
57        )
58    }
59
60    /// Convert event to ASS string with specific format
61    ///
62    /// Generates an ASS event line according to the provided format specification.
63    /// This allows handling both V4+ and V4++ formats, as well as custom formats.
64    ///
65    /// # Arguments
66    ///
67    /// * `format` - Field names in order (e.g., `["Layer", "Start", "End", "Style", "Text"]`)
68    ///
69    /// # Examples
70    ///
71    /// ```rust
72    /// # use ass_core::parser::ast::{Event, EventType};
73    /// let event = Event {
74    ///     event_type: EventType::Comment,
75    ///     start: "0:00:00.00",
76    ///     end: "0:00:05.00",
77    ///     text: "Note",
78    ///     ..Event::default()
79    /// };
80    /// let format = vec!["Start", "End", "Text"];
81    /// assert_eq!(
82    ///     event.to_ass_string_with_format(&format),
83    ///     "Comment: 0:00:00.00,0:00:05.00,Note"
84    /// );
85    /// ```
86    #[must_use]
87    pub fn to_ass_string_with_format(&self, format: &[&str]) -> alloc::string::String {
88        let event_type_str = self.event_type.as_str();
89        let mut field_values = Vec::with_capacity(format.len());
90
91        for field in format {
92            let value = match *field {
93                "Layer" => self.layer,
94                "Start" => self.start,
95                "End" => self.end,
96                "Style" => self.style,
97                "Name" | "Actor" => self.name,
98                "MarginL" => self.margin_l,
99                "MarginR" => self.margin_r,
100                "MarginV" => self.margin_v,
101                "MarginT" => self.margin_t.unwrap_or("0"),
102                "MarginB" => self.margin_b.unwrap_or("0"),
103                "Effect" => self.effect,
104                "Text" => self.text,
105                _ => "", // Unknown fields default to empty
106            };
107            field_values.push(value);
108        }
109
110        format!("{event_type_str}: {}", field_values.join(","))
111    }
112
113    /// Validate all spans in this Event reference valid source
114    ///
115    /// Debug helper to ensure zero-copy invariants are maintained.
116    /// Validates that all string references point to memory within
117    /// the specified source range.
118    ///
119    /// Only available in debug builds to avoid performance overhead.
120    #[cfg(debug_assertions)]
121    #[must_use]
122    pub fn validate_spans(&self, source_range: &Range<usize>) -> bool {
123        let spans = [
124            self.layer,
125            self.start,
126            self.end,
127            self.style,
128            self.name,
129            self.margin_l,
130            self.margin_r,
131            self.margin_v,
132            self.effect,
133            self.text,
134        ];
135
136        spans.iter().all(|span| {
137            let ptr = span.as_ptr() as usize;
138            source_range.contains(&ptr)
139        })
140    }
141}