Skip to main content

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}