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}