ass_core/parser/ast/style/conversion.rs
1//! String conversion and span-validation methods for the `Style` node.
2//!
3//! Provides ASS string serialization helpers (`to_ass_string`,
4//! `to_ass_string_with_format`) and the debug-only `validate_spans` invariant
5//! check for the zero-copy `Style` struct.
6
7#[cfg(not(feature = "std"))]
8extern crate alloc;
9#[cfg(not(feature = "std"))]
10use alloc::{format, vec::Vec};
11
12use super::Style;
13#[cfg(debug_assertions)]
14use core::ops::Range;
15
16impl Style<'_> {
17 /// Convert style to ASS string representation
18 ///
19 /// Generates the standard ASS style line format for V4+ styles.
20 /// Uses `margin_v` by default, but will use `margin_t/margin_b` if provided (V4++ format).
21 ///
22 /// # Examples
23 ///
24 /// ```rust
25 /// # use ass_core::parser::ast::Style;
26 /// let style = Style {
27 /// name: "TestStyle",
28 /// fontname: "Arial",
29 /// fontsize: "20",
30 /// ..Style::default()
31 /// };
32 /// let ass_string = style.to_ass_string();
33 /// assert!(ass_string.starts_with("Style: TestStyle,Arial,20,"));
34 /// ```
35 #[must_use]
36 pub fn to_ass_string(&self) -> alloc::string::String {
37 // Use standard V4+ format by default
38 // TODO: Support custom format lines
39 format!(
40 "Style: {},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}",
41 self.name,
42 self.fontname,
43 self.fontsize,
44 self.primary_colour,
45 self.secondary_colour,
46 self.outline_colour,
47 self.back_colour,
48 self.bold,
49 self.italic,
50 self.underline,
51 self.strikeout,
52 self.scale_x,
53 self.scale_y,
54 self.spacing,
55 self.angle,
56 self.border_style,
57 self.outline,
58 self.shadow,
59 self.alignment,
60 self.margin_l,
61 self.margin_r,
62 self.margin_v,
63 self.encoding
64 )
65 }
66
67 /// Convert style to ASS string with specific format
68 ///
69 /// Generates an ASS style line according to the provided format specification.
70 /// This allows handling both V4+ and V4++ formats, as well as custom formats.
71 ///
72 /// # Arguments
73 ///
74 /// * `format` - Field names in order (e.g., ["Name", "Fontname", "Fontsize", ...])
75 ///
76 /// # Examples
77 ///
78 /// ```rust
79 /// # use ass_core::parser::ast::Style;
80 /// let style = Style {
81 /// name: "Simple",
82 /// fontname: "Arial",
83 /// fontsize: "16",
84 /// ..Style::default()
85 /// };
86 /// let format = vec!["Name", "Fontname", "Fontsize"];
87 /// assert_eq!(
88 /// style.to_ass_string_with_format(&format),
89 /// "Style: Simple,Arial,16"
90 /// );
91 /// ```
92 #[must_use]
93 pub fn to_ass_string_with_format(&self, format: &[&str]) -> alloc::string::String {
94 let mut field_values = Vec::with_capacity(format.len());
95
96 for field in format {
97 let value = match *field {
98 "Name" => self.name,
99 "Fontname" => self.fontname,
100 "Fontsize" => self.fontsize,
101 "PrimaryColour" => self.primary_colour,
102 "SecondaryColour" => self.secondary_colour,
103 "OutlineColour" | "TertiaryColour" => self.outline_colour,
104 "BackColour" => self.back_colour,
105 "Bold" => self.bold,
106 "Italic" => self.italic,
107 "Underline" => self.underline,
108 "Strikeout" | "StrikeOut" => self.strikeout,
109 "ScaleX" => self.scale_x,
110 "ScaleY" => self.scale_y,
111 "Spacing" => self.spacing,
112 "Angle" => self.angle,
113 "BorderStyle" => self.border_style,
114 "Outline" => self.outline,
115 "Shadow" => self.shadow,
116 "Alignment" => self.alignment,
117 "MarginL" => self.margin_l,
118 "MarginR" => self.margin_r,
119 "MarginV" => self.margin_v,
120 "MarginT" => self.margin_t.unwrap_or("0"),
121 "MarginB" => self.margin_b.unwrap_or("0"),
122 "Encoding" => self.encoding,
123 "RelativeTo" => self.relative_to.unwrap_or("0"),
124 _ => "", // Unknown fields default to empty
125 };
126 field_values.push(value);
127 }
128
129 let joined = field_values.join(",");
130 format!("Style: {joined}")
131 }
132
133 /// Validate all spans in this Style reference valid source
134 ///
135 /// Debug helper to ensure zero-copy invariants are maintained.
136 /// Validates that all string references point to memory within
137 /// the specified source range.
138 ///
139 /// Only available in debug builds to avoid performance overhead.
140 #[cfg(debug_assertions)]
141 #[must_use]
142 pub fn validate_spans(&self, source_range: &Range<usize>) -> bool {
143 let spans = [
144 self.name,
145 self.fontname,
146 self.fontsize,
147 self.primary_colour,
148 self.secondary_colour,
149 self.outline_colour,
150 self.back_colour,
151 self.bold,
152 self.italic,
153 self.underline,
154 self.strikeout,
155 self.scale_x,
156 self.scale_y,
157 self.spacing,
158 self.angle,
159 self.border_style,
160 self.outline,
161 self.shadow,
162 self.alignment,
163 self.margin_l,
164 self.margin_r,
165 self.margin_v,
166 self.encoding,
167 ];
168
169 spans.iter().all(|span| {
170 let ptr = span.as_ptr() as usize;
171 source_range.contains(&ptr)
172 })
173 }
174}