Skip to main content

vexy_vsvg/parser/
main.rs

1// this_file: crates/vexy-vsvg/src/parser/main.rs
2
3//! SVG parser using quick-xml
4//!
5//! This module provides functionality to parse SVG strings into our custom AST
6//! using the quick-xml crate for fast streaming XML parsing.
7//!
8//! Features streaming parsing optimizations for large documents,
9//! efficient buffer management, and memory-conscious parsing strategies.
10
11use std::io::{BufRead, BufReader};
12
13use crate::ast::Document;
14use crate::error::VexyError;
15use crate::parser::config::StreamingConfig;
16use crate::parser::streaming::StreamingParser;
17
18/// SVG parser with streaming optimizations
19pub struct Parser {
20    /// Whether to preserve whitespace
21    preserve_whitespace: bool,
22    /// Whether to preserve comments
23    preserve_comments: bool,
24    /// Whether to expand XML entities
25    expand_entities: bool,
26
27    /// Streaming configuration
28    streaming_config: StreamingConfig,
29}
30
31impl Parser {
32    /// Create a new parser with default settings
33    pub fn new() -> Self {
34        Self {
35            preserve_whitespace: false,
36            preserve_comments: true,
37            expand_entities: true,
38            streaming_config: StreamingConfig::default(),
39        }
40    }
41
42    /// Create a new parser with streaming configuration
43    pub fn with_streaming_config(config: StreamingConfig) -> Self {
44        Self {
45            preserve_whitespace: false,
46            preserve_comments: true,
47            expand_entities: true,
48            streaming_config: config,
49        }
50    }
51
52    /// Set whether to preserve whitespace
53    pub fn preserve_whitespace(mut self, preserve: bool) -> Self {
54        self.preserve_whitespace = preserve;
55        self
56    }
57
58    /// Set whether to preserve comments
59    pub fn preserve_comments(mut self, preserve: bool) -> Self {
60        self.preserve_comments = preserve;
61        self
62    }
63
64    /// Set whether to expand XML entities
65    pub fn expand_entities(mut self, expand: bool) -> Self {
66        self.expand_entities = expand;
67        self
68    }
69
70    /// Set streaming configuration
71    pub fn streaming_config(mut self, config: StreamingConfig) -> Self {
72        self.streaming_config = config;
73        self
74    }
75
76    /// Parse an SVG string into a Document (static method for convenience)
77    pub fn parse_svg_string(input: &str) -> Result<Document<'static>, VexyError> {
78        let parser = Self::new();
79        parser.parse(input)
80    }
81
82    /// Parse an SVG string into a Document
83    /// For large documents, consider using `parse_streaming` for better memory efficiency
84    pub fn parse(&self, input: &str) -> Result<Document<'static>, VexyError> {
85        // For very large inputs, automatically use streaming parser
86        if input.len() > self.streaming_config.buffer_size * 4 {
87            return self.parse_streaming_from_str(input);
88        }
89
90        self.parse_internal(input)
91    }
92
93    /// Internal parsing implementation
94    fn parse_internal(&self, input: &str) -> Result<Document<'static>, VexyError> {
95        let mut streaming_parser = StreamingParser::new(
96            BufReader::new(input.as_bytes()),
97            self.streaming_config.clone(),
98            self.preserve_whitespace,
99            self.preserve_comments,
100            self.expand_entities,
101            None,
102        );
103        streaming_parser.parse()
104    }
105
106    /// Parse from a streaming source for memory efficiency with large files
107    pub fn parse_streaming<R: BufRead>(&self, reader: R) -> Result<Document<'static>, VexyError> {
108        let mut streaming_parser = StreamingParser::new(
109            reader,
110            self.streaming_config.clone(),
111            self.preserve_whitespace,
112            self.preserve_comments,
113            self.expand_entities,
114            None,
115        );
116        streaming_parser.parse()
117    }
118
119    /// Parse from a large string using streaming approach
120    fn parse_streaming_from_str(&self, input: &str) -> Result<Document<'static>, VexyError> {
121        let cursor = std::io::Cursor::new(input);
122        let buf_reader = BufReader::with_capacity(self.streaming_config.buffer_size, cursor);
123        self.parse_streaming(buf_reader)
124    }
125}
126
127impl Default for Parser {
128    fn default() -> Self {
129        Self::new()
130    }
131}
132
133/// Convenience function to parse an SVG string
134pub fn parse_svg(input: &str) -> Result<Document<'static>, VexyError> {
135    Parser::new().parse(input)
136}
137
138/// Convenience function to parse a large SVG with streaming
139pub fn parse_svg_streaming<R: BufRead>(reader: R) -> Result<Document<'static>, VexyError> {
140    Parser::new().parse_streaming(reader)
141}
142
143/// Parse SVG from a file with automatic streaming for large files
144pub fn parse_svg_file<P: AsRef<std::path::Path>>(path: P) -> Result<Document<'static>, VexyError> {
145    let file = std::fs::File::open(&path)?;
146    let metadata = file.metadata()?;
147
148    let parser = Parser::new();
149
150    // Use streaming for files larger than 1MB
151    if metadata.len() > 1024 * 1024 {
152        let buf_reader = BufReader::with_capacity(128 * 1024, file);
153        parser.parse_streaming(buf_reader)
154    } else {
155        let content = std::fs::read_to_string(path)?;
156        parser.parse(&content)
157    }
158}