Skip to main content

lex_babel/formats/lex/
mod.rs

1//! Lex format implementation
2//!
3//! This module implements the Format trait for Lex itself, treating Lex
4//! as just another format in the system. This creates a uniform API where
5//! Lex can be converted to/from other formats using the same interface.
6
7use crate::error::FormatError;
8use crate::format::Format;
9use lex_core::lex::ast::Document;
10use lex_core::lex::transforms::standard::STRING_TO_AST;
11
12mod annotation_inline;
13mod blank_coalesce;
14pub mod formatting_rules;
15pub mod serializer;
16
17use annotation_inline::inline_attached_annotations;
18use blank_coalesce::coalesce_blank_line_groups;
19use formatting_rules::FormattingRules;
20use serializer::LexSerializer;
21///
22/// Parses Lex source text into a Document AST by delegating to lex-parser.
23/// Serialization is implemented via LexSerializer.
24#[derive(Default)]
25pub struct LexFormat {
26    rules: FormattingRules,
27}
28
29impl LexFormat {
30    pub fn new(rules: FormattingRules) -> Self {
31        Self { rules }
32    }
33}
34
35impl Format for LexFormat {
36    fn name(&self) -> &str {
37        "lex"
38    }
39
40    fn description(&self) -> &str {
41        "Lex document format"
42    }
43
44    fn file_extensions(&self) -> &[&str] {
45        &["lex"]
46    }
47
48    fn supports_parsing(&self) -> bool {
49        true
50    }
51
52    fn supports_serialization(&self) -> bool {
53        true
54    }
55
56    fn parse(&self, source: &str) -> Result<Document, FormatError> {
57        STRING_TO_AST
58            .run(source.to_string())
59            .map_err(|e| FormatError::ParseError(e.to_string()))
60    }
61
62    fn serialize(&self, doc: &Document) -> Result<String, FormatError> {
63        // Operate on a clone to keep the `&Document` contract. Re-insert
64        // annotations the parser attached to elements so they aren't dropped
65        // (lex#682), then merge adjacent blank-line groups so blank-run emission
66        // (including blanks the inlining introduces) is idempotent (lex#686).
67        let mut doc = doc.clone();
68        inline_attached_annotations(&mut doc);
69        coalesce_blank_line_groups(&mut doc);
70        let serializer = LexSerializer::new(self.rules.clone());
71        serializer
72            .serialize(&doc)
73            .map_err(FormatError::SerializationError)
74    }
75}
76
77/// Convenience function to export a Document AST into a Lex string
78/// using the default formatting rules.
79pub fn export(doc: &Document) -> Result<String, FormatError> {
80    LexFormat::default().serialize(doc)
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86    use lex_core::lex::ast::{ContentItem, Paragraph};
87
88    #[test]
89    fn test_lex_format_name() {
90        let format = LexFormat::default();
91        assert_eq!(format.name(), "lex");
92    }
93
94    #[test]
95    fn test_lex_format_supports_parsing() {
96        let format = LexFormat::default();
97        assert!(format.supports_parsing());
98        assert!(format.supports_serialization());
99    }
100
101    #[test]
102    fn test_lex_format_parse_simple() {
103        let format = LexFormat::default();
104        let source = "Hello world\n";
105
106        let result = format.parse(source);
107        assert!(result.is_ok());
108
109        let doc = result.unwrap();
110        assert_eq!(doc.root.children.len(), 1);
111
112        match &doc.root.children[0] {
113            ContentItem::Paragraph(_) => {}
114            _ => panic!("Expected paragraph"),
115        }
116    }
117
118    #[test]
119    fn test_lex_format_parse_session() {
120        let format = LexFormat::default();
121        let source = "Introduction:\n    Welcome to the guide\n";
122
123        let result = format.parse(source);
124        assert!(result.is_ok());
125
126        let doc = result.unwrap();
127        // Just verify that something was parsed successfully
128        // The exact structure depends on the parser implementation
129        assert!(!doc.root.children.is_empty());
130    }
131
132    #[test]
133    fn test_lex_format_parse_error() {
134        let format = LexFormat::default();
135        // Create invalid Lex that would cause a parse error
136        // Note: Current parser is very permissive, so this might not fail
137        // But the test shows the error handling works
138        let source = "";
139
140        let result = format.parse(source);
141        // Empty document should parse successfully
142        assert!(result.is_ok());
143    }
144
145    #[test]
146    fn test_lex_format_serialize_supported() {
147        let format = LexFormat::default();
148        let doc = Document::with_content(vec![ContentItem::Paragraph(Paragraph::from_line(
149            "Test".to_string(),
150        ))]);
151
152        let result = format.serialize(&doc);
153        assert!(result.is_ok());
154        assert_eq!(result.unwrap(), "Test\n");
155    }
156}