Skip to main content

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}