ass_core/parser/ast/media/font.rs
1//! Embedded font AST node for the `[Fonts]` section.
2//!
3//! Defines the [`Font`] struct with lazy UU-decoding helpers and zero-copy
4//! spans over the original ASS source text.
5
6#[cfg(not(feature = "std"))]
7use alloc::format;
8use alloc::vec::Vec;
9
10use super::super::Span;
11#[cfg(debug_assertions)]
12use core::ops::Range;
13
14/// Embedded font from `[Fonts\]` section
15///
16/// Represents a font file embedded in the ASS script using UU-encoding.
17/// Provides lazy decoding to avoid processing overhead unless the font
18/// data is actually needed.
19///
20/// # Examples
21///
22/// ```rust
23/// use ass_core::parser::ast::{Font, Span};
24///
25/// let font = Font {
26/// filename: "custom.ttf",
27/// data_lines: vec!["begin 644 custom.ttf", "M'XL..."],
28/// span: Span::new(0, 0, 0, 0),
29/// };
30///
31/// // Decode when needed
32/// let decoded = font.decode_data()?;
33/// # Ok::<(), Box<dyn std::error::Error>>(())
34/// ```
35#[derive(Debug, Clone, PartialEq, Eq)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize))]
37pub struct Font<'a> {
38 /// Font filename as it appears in the `[Fonts\]` section
39 pub filename: &'a str,
40
41 /// UU-encoded font data lines as zero-copy spans
42 pub data_lines: Vec<&'a str>,
43
44 /// Span in source text where this font is defined
45 pub span: Span,
46}
47
48impl Font<'_> {
49 /// Decode UU-encoded font data with lazy evaluation
50 ///
51 /// Converts the UU-encoded data lines to raw binary font data.
52 /// This is expensive so it's only done when explicitly requested.
53 ///
54 /// # Returns
55 ///
56 /// Decoded binary font data on success, error if UU-decoding fails
57 ///
58 /// # Errors
59 ///
60 /// Returns an error if the UU-encoded data is malformed or cannot be decoded.
61 ///
62 /// # Examples
63 ///
64 /// ```rust
65 /// # use ass_core::parser::ast::{Font, Span};
66 /// # let font = Font { filename: "test.ttf", data_lines: vec![], span: Span::new(0, 0, 0, 0) };
67 /// match font.decode_data() {
68 /// Ok(data) => println!("Font size: {} bytes", data.len()),
69 /// Err(e) => eprintln!("Decode error: {}", e),
70 /// }
71 /// ```
72 pub fn decode_data(&self) -> Result<Vec<u8>, crate::utils::CoreError> {
73 crate::utils::decode_uu_data(self.data_lines.iter().copied())
74 }
75
76 /// Convert font to ASS string representation
77 ///
78 /// Generates the font entry as it appears in the `[Fonts\]` section.
79 ///
80 /// # Examples
81 ///
82 /// ```rust
83 /// # use ass_core::parser::ast::{Font, Span};
84 /// let font = Font {
85 /// filename: "custom.ttf",
86 /// data_lines: vec!["begin 644 custom.ttf", "M'XL...", "end"],
87 /// span: Span::new(0, 0, 0, 0),
88 /// };
89 /// let ass_string = font.to_ass_string();
90 /// assert!(ass_string.starts_with("fontname: custom.ttf\n"));
91 /// assert!(ass_string.contains("M'XL..."));
92 /// ```
93 #[must_use]
94 pub fn to_ass_string(&self) -> alloc::string::String {
95 let mut result = format!("fontname: {}\n", self.filename);
96 for line in &self.data_lines {
97 result.push_str(line);
98 result.push('\n');
99 }
100 result
101 }
102
103 /// Validate all spans in this Font reference valid source
104 ///
105 /// Debug helper to ensure zero-copy invariants are maintained.
106 /// Validates that filename and all data line references point to
107 /// memory within the specified source range.
108 ///
109 /// Only available in debug builds to avoid performance overhead.
110 #[cfg(debug_assertions)]
111 #[must_use]
112 pub fn validate_spans(&self, source_range: &Range<usize>) -> bool {
113 let filename_ptr = self.filename.as_ptr() as usize;
114 let filename_valid = source_range.contains(&filename_ptr);
115
116 let data_valid = self.data_lines.iter().all(|line| {
117 let ptr = line.as_ptr() as usize;
118 source_range.contains(&ptr)
119 });
120
121 filename_valid && data_valid
122 }
123}