unistructgen_core/parser.rs
1//! Parser trait and related types
2//!
3//! This module defines the core trait that all parsers must implement.
4//! It provides a unified interface for parsing different input formats
5//! (JSON, Markdown, SQL, etc.) into the Intermediate Representation (IR).
6
7use crate::IRModule;
8use std::error::Error as StdError;
9
10/// Result type alias for parser operations
11pub type ParserResult<T> = std::result::Result<T, Box<dyn StdError + Send + Sync>>;
12
13/// Core trait for all parsers
14///
15/// Implement this trait to add support for new input formats.
16/// The parser is responsible for converting input text into an IR module
17/// that can be processed by code generators.
18///
19/// # Type Parameters
20///
21/// * `Error` - The error type produced by this parser. Must implement
22/// `std::error::Error + Send + Sync + 'static` for composability.
23///
24/// # Examples
25///
26/// ```ignore
27/// use unistructgen_core::{Parser, IRModule};
28///
29/// struct MyParser {
30/// options: MyOptions,
31/// }
32///
33/// impl Parser for MyParser {
34/// type Error = MyParserError;
35///
36/// fn parse(&mut self, input: &str) -> Result<IRModule, Self::Error> {
37/// // Parse input and return IR
38/// todo!()
39/// }
40///
41/// fn name(&self) -> &'static str {
42/// "MyFormat"
43/// }
44///
45/// fn extensions(&self) -> &[&'static str] {
46/// &["my", "myformat"]
47/// }
48/// }
49/// ```
50pub trait Parser {
51 /// The error type this parser produces
52 type Error: StdError + Send + Sync + 'static;
53
54 /// Parse input text and return an IR module
55 ///
56 /// # Arguments
57 ///
58 /// * `input` - The input text to parse
59 ///
60 /// # Returns
61 ///
62 /// Returns `Ok(IRModule)` on success, containing the parsed types.
63 ///
64 /// # Errors
65 ///
66 /// Returns `Self::Error` if parsing fails. The error should contain
67 /// detailed information about what went wrong and where.
68 fn parse(&mut self, input: &str) -> Result<IRModule, Self::Error>;
69
70 /// Get the parser's human-readable name
71 ///
72 /// Used for diagnostics and error messages.
73 ///
74 /// # Examples
75 ///
76 /// ```ignore
77 /// assert_eq!(json_parser.name(), "JSON");
78 /// ```
79 fn name(&self) -> &'static str;
80
81 /// Get the file extensions this parser supports
82 ///
83 /// Used for automatic format detection based on file extension.
84 ///
85 /// # Examples
86 ///
87 /// ```ignore
88 /// assert_eq!(json_parser.extensions(), &["json"]);
89 /// assert_eq!(markdown_parser.extensions(), &["md", "markdown"]);
90 /// ```
91 fn extensions(&self) -> &[&'static str];
92
93 /// Validate input without full parsing (optional)
94 ///
95 /// Provides a quick way to check if input is valid without
96 /// performing full parsing. Default implementation always returns `Ok(())`.
97 ///
98 /// # Arguments
99 ///
100 /// * `input` - The input text to validate
101 ///
102 /// # Returns
103 ///
104 /// Returns `Ok(())` if input appears valid, `Err` otherwise.
105 fn validate(&self, _input: &str) -> Result<(), Self::Error> {
106 Ok(())
107 }
108
109 /// Get metadata about the parser (optional)
110 ///
111 /// Returns additional information about the parser's capabilities,
112 /// version, etc. Default implementation returns empty metadata.
113 fn metadata(&self) -> ParserMetadata {
114 ParserMetadata::default()
115 }
116}
117
118/// Metadata about a parser
119///
120/// Contains additional information about parser capabilities and configuration.
121#[derive(Debug, Clone, Default)]
122pub struct ParserMetadata {
123 /// Parser version
124 pub version: Option<String>,
125 /// Description of what this parser does
126 pub description: Option<String>,
127 /// List of supported features
128 pub features: Vec<String>,
129 /// Additional custom metadata
130 pub custom: std::collections::HashMap<String, String>,
131}
132
133impl ParserMetadata {
134 /// Create new empty metadata
135 pub fn new() -> Self {
136 Self::default()
137 }
138
139 /// Set version
140 pub fn with_version(mut self, version: impl Into<String>) -> Self {
141 self.version = Some(version.into());
142 self
143 }
144
145 /// Set description
146 pub fn with_description(mut self, description: impl Into<String>) -> Self {
147 self.description = Some(description.into());
148 self
149 }
150
151 /// Add a feature
152 pub fn with_feature(mut self, feature: impl Into<String>) -> Self {
153 self.features.push(feature.into());
154 self
155 }
156
157 /// Add custom metadata
158 pub fn with_custom(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
159 self.custom.insert(key.into(), value.into());
160 self
161 }
162}
163
164/// Helper trait for converting errors to parser results
165///
166/// Allows using `?` operator with different error types.
167pub trait IntoParserError {
168 /// Convert into a boxed error
169 fn into_parser_error(self) -> Box<dyn StdError + Send + Sync>;
170}
171
172impl<E> IntoParserError for E
173where
174 E: StdError + Send + Sync + 'static,
175{
176 fn into_parser_error(self) -> Box<dyn StdError + Send + Sync> {
177 Box::new(self)
178 }
179}
180
181/// Extension trait for Parser to provide convenience methods
182pub trait ParserExt: Parser {
183 /// Parse input and convert to a specific error type
184 fn parse_or<E>(&mut self, input: &str) -> Result<IRModule, E>
185 where
186 E: From<Self::Error>,
187 {
188 self.parse(input).map_err(E::from)
189 }
190
191 /// Parse input with validation first
192 fn parse_validated(&mut self, input: &str) -> Result<IRModule, Self::Error> {
193 self.validate(input)?;
194 self.parse(input)
195 }
196}
197
198// Blanket implementation for all Parser types
199impl<P: Parser> ParserExt for P {}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204 use crate::{IRModule, IRStruct, IRType};
205
206 // Mock parser for testing
207 struct MockParser;
208
209 #[derive(Debug)]
210 struct MockError;
211
212 impl std::fmt::Display for MockError {
213 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
214 write!(f, "Mock error")
215 }
216 }
217
218 impl StdError for MockError {}
219
220 impl Parser for MockParser {
221 type Error = MockError;
222
223 fn parse(&mut self, _input: &str) -> Result<IRModule, Self::Error> {
224 let mut module = IRModule::new("Test".to_string());
225 let test_struct = IRStruct::new("Test".to_string());
226 module.add_type(IRType::Struct(test_struct));
227 Ok(module)
228 }
229
230 fn name(&self) -> &'static str {
231 "Mock"
232 }
233
234 fn extensions(&self) -> &[&'static str] {
235 &["mock"]
236 }
237 }
238
239 #[test]
240 fn test_parser_trait() {
241 let mut parser = MockParser;
242 let result = parser.parse("test input");
243 assert!(result.is_ok());
244
245 let module = result.unwrap();
246 assert_eq!(module.name, "Test");
247 assert_eq!(module.types.len(), 1);
248 }
249
250 #[test]
251 fn test_parser_metadata() {
252 let parser = MockParser;
253 assert_eq!(parser.name(), "Mock");
254 assert_eq!(parser.extensions(), &["mock"]);
255 }
256
257 #[test]
258 fn test_parser_metadata_builder() {
259 let metadata = ParserMetadata::new()
260 .with_version("1.0.0")
261 .with_description("Test parser")
262 .with_feature("nested-objects")
263 .with_custom("author", "test");
264
265 assert_eq!(metadata.version, Some("1.0.0".to_string()));
266 assert_eq!(metadata.description, Some("Test parser".to_string()));
267 assert_eq!(metadata.features, vec!["nested-objects"]);
268 assert_eq!(metadata.custom.get("author"), Some(&"test".to_string()));
269 }
270
271 #[test]
272 fn test_parser_ext() {
273 let mut parser = MockParser;
274 let result = parser.parse_validated("test");
275 assert!(result.is_ok());
276 }
277}