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