Skip to main content

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}