1use crate::error::FormatError;
7use crate::format::{Format, SerializedDocument};
8use lex_core::lex::ast::Document;
9use std::collections::HashMap;
10
11pub struct FormatRegistry {
26 formats: HashMap<String, Box<dyn Format>>,
27}
28
29impl FormatRegistry {
30 pub fn new() -> Self {
32 FormatRegistry {
33 formats: HashMap::new(),
34 }
35 }
36
37 pub fn register<F: Format + 'static>(&mut self, format: F) {
41 self.formats
42 .insert(format.name().to_string(), Box::new(format));
43 }
44
45 pub fn get(&self, name: &str) -> Result<&dyn Format, FormatError> {
47 self.formats
48 .get(name)
49 .map(|f| f.as_ref())
50 .ok_or_else(|| FormatError::FormatNotFound(name.to_string()))
51 }
52
53 pub fn has(&self, name: &str) -> bool {
55 self.formats.contains_key(name)
56 }
57
58 pub fn list_formats(&self) -> Vec<String> {
60 let mut names: Vec<_> = self.formats.keys().cloned().collect();
61 names.sort();
62 names
63 }
64
65 pub fn detect_format_from_filename(&self, filename: &str) -> Option<String> {
78 let extension = std::path::Path::new(filename)
80 .extension()
81 .and_then(|ext| ext.to_str())?;
82
83 for format in self.formats.values() {
85 if format.file_extensions().contains(&extension) {
86 return Some(format.name().to_string());
87 }
88 }
89
90 None
91 }
92
93 pub fn parse(&self, source: &str, format: &str) -> Result<Document, FormatError> {
95 let fmt = self.get(format)?;
96 if !fmt.supports_parsing() {
97 return Err(FormatError::NotSupported(format!(
98 "Format '{format}' does not support parsing"
99 )));
100 }
101 fmt.parse(source)
102 }
103
104 pub fn serialize(&self, doc: &Document, format: &str) -> Result<String, FormatError> {
106 let empty = HashMap::new();
107 match self.serialize_with_options(doc, format, &empty)? {
108 SerializedDocument::Text(text) => Ok(text),
109 SerializedDocument::Binary(_) => Err(FormatError::SerializationError(format!(
110 "Format '{format}' produced binary output when text was expected"
111 ))),
112 }
113 }
114
115 pub fn serialize_with_options(
117 &self,
118 doc: &Document,
119 format: &str,
120 options: &HashMap<String, String>,
121 ) -> Result<SerializedDocument, FormatError> {
122 let fmt = self.get(format)?;
123 if !fmt.supports_serialization() {
124 return Err(FormatError::NotSupported(format!(
125 "Format '{format}' does not support serialization"
126 )));
127 }
128 fmt.serialize_with_options(doc, options)
129 }
130
131 pub fn with_defaults() -> Self {
133 let mut registry = Self::new();
134
135 registry.register(crate::formats::lex::LexFormat::default());
137 registry.register(crate::formats::html::HtmlFormat::default());
138 #[cfg(feature = "native-export")]
139 registry.register(crate::formats::pdf::PdfFormat::default());
140 #[cfg(feature = "native-export")]
141 registry.register(crate::formats::png::PngFormat::default());
142 registry.register(crate::formats::markdown::MarkdownFormat);
143 registry.register(crate::formats::rfc_xml::RfcXmlFormat);
144 registry.register(crate::formats::tag::TagFormat);
145 registry.register(crate::formats::treeviz::TreevizFormat);
146 registry.register(crate::formats::linetreeviz::LinetreevizFormat);
147
148 registry
149 }
150}
151
152impl Default for FormatRegistry {
153 fn default() -> Self {
154 Self::with_defaults()
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 use crate::format::Format;
162 use lex_core::lex::ast::{ContentItem, Document, Paragraph};
163
164 struct TestFormat;
166 impl Format for TestFormat {
167 fn name(&self) -> &str {
168 "test"
169 }
170 fn description(&self) -> &str {
171 "Test format"
172 }
173 fn supports_parsing(&self) -> bool {
174 true
175 }
176 fn supports_serialization(&self) -> bool {
177 true
178 }
179 fn parse(&self, _source: &str) -> Result<Document, FormatError> {
180 Ok(Document::with_content(vec![ContentItem::Paragraph(
181 Paragraph::from_line("test".to_string()),
182 )]))
183 }
184 fn serialize(&self, _doc: &Document) -> Result<String, FormatError> {
185 Ok("test output".to_string())
186 }
187 }
188
189 #[test]
190 fn test_registry_creation() {
191 let registry = FormatRegistry::new();
192 assert_eq!(registry.formats.len(), 0);
193 }
194
195 #[test]
196 fn test_registry_register() {
197 let mut registry = FormatRegistry::new();
198 registry.register(TestFormat);
199
200 assert!(registry.has("test"));
201 assert_eq!(registry.list_formats(), vec!["test"]);
202 }
203
204 #[test]
205 fn test_registry_get() {
206 let mut registry = FormatRegistry::new();
207 registry.register(TestFormat);
208
209 let format = registry.get("test");
210 assert!(format.is_ok());
211 assert_eq!(format.unwrap().name(), "test");
212 }
213
214 #[test]
215 fn test_registry_get_nonexistent() {
216 let registry = FormatRegistry::new();
217 let result = registry.get("nonexistent");
218 assert!(result.is_err());
219 }
220
221 #[test]
222 fn test_registry_has() {
223 let mut registry = FormatRegistry::new();
224 registry.register(TestFormat);
225
226 assert!(registry.has("test"));
227 assert!(!registry.has("nonexistent"));
228 }
229
230 #[test]
231 fn test_registry_parse() {
232 let mut registry = FormatRegistry::new();
233 registry.register(TestFormat);
234
235 let result = registry.parse("input", "test");
236 assert!(result.is_ok());
237 }
238
239 #[test]
240 fn test_registry_parse_not_found() {
241 let registry = FormatRegistry::new();
242
243 let result = registry.parse("input", "nonexistent");
244 assert!(result.is_err());
245 match result.unwrap_err() {
246 FormatError::FormatNotFound(name) => assert_eq!(name, "nonexistent"),
247 _ => panic!("Expected FormatNotFound error"),
248 }
249 }
250
251 #[test]
252 fn test_registry_serialize() {
253 let mut registry = FormatRegistry::new();
254 registry.register(TestFormat);
255
256 let doc = Document::with_content(vec![ContentItem::Paragraph(Paragraph::from_line(
257 "Hello".to_string(),
258 ))]);
259
260 let result = registry.serialize(&doc, "test");
261 assert!(result.is_ok());
262 assert_eq!(result.unwrap(), "test output");
263 }
264
265 #[test]
266 fn test_registry_serialize_not_found() {
267 let registry = FormatRegistry::new();
268 let doc = Document::with_content(vec![]);
269
270 let result = registry.serialize(&doc, "nonexistent");
271 assert!(result.is_err());
272 match result.unwrap_err() {
273 FormatError::FormatNotFound(name) => assert_eq!(name, "nonexistent"),
274 _ => panic!("Expected FormatNotFound error"),
275 }
276 }
277
278 #[test]
279 fn test_registry_serialize_with_options_default_behavior() {
280 let mut registry = FormatRegistry::new();
281 registry.register(TestFormat);
282
283 let doc = Document::with_content(vec![ContentItem::Paragraph(Paragraph::from_line(
284 "Hello".to_string(),
285 ))]);
286 let mut options = HashMap::new();
287 options.insert("unused".to_string(), "true".to_string());
288
289 let result = registry.serialize_with_options(&doc, "test", &options);
290 assert!(result.is_err());
291 }
292
293 #[test]
294 fn test_registry_list_formats() {
295 let mut registry = FormatRegistry::new();
296 registry.register(TestFormat);
297
298 let formats = registry.list_formats();
299 assert_eq!(formats.len(), 1);
300 assert_eq!(formats[0], "test");
301 }
302
303 #[test]
304 fn test_registry_with_defaults() {
305 let registry = FormatRegistry::with_defaults();
306 assert!(registry.has("lex"));
307 assert!(registry.has("markdown"));
308 assert!(registry.has("tag"));
309 assert!(registry.has("treeviz"));
310 }
311
312 #[test]
313 fn test_registry_default_trait() {
314 let registry = FormatRegistry::default();
315 assert!(registry.has("lex"));
316 assert!(registry.has("markdown"));
317 assert!(registry.has("tag"));
318 assert!(registry.has("treeviz"));
319 }
320
321 #[test]
322 fn test_registry_replace_format() {
323 let mut registry = FormatRegistry::new();
324 registry.register(TestFormat);
325 registry.register(TestFormat); assert_eq!(registry.list_formats().len(), 1);
328 }
329
330 #[test]
331 fn test_detect_format_from_filename() {
332 let registry = FormatRegistry::with_defaults();
333
334 assert_eq!(
336 registry.detect_format_from_filename("doc.lex"),
337 Some("lex".to_string())
338 );
339 assert_eq!(
340 registry.detect_format_from_filename("/path/to/file.lex"),
341 Some("lex".to_string())
342 );
343
344 assert_eq!(
346 registry.detect_format_from_filename("doc.tag"),
347 Some("tag".to_string())
348 );
349 assert_eq!(
350 registry.detect_format_from_filename("doc.xml"),
351 Some("tag".to_string())
352 );
353
354 assert_eq!(
356 registry.detect_format_from_filename("doc.tree"),
357 Some("treeviz".to_string())
358 );
359 assert_eq!(
360 registry.detect_format_from_filename("doc.treeviz"),
361 Some("treeviz".to_string())
362 );
363
364 assert_eq!(registry.detect_format_from_filename("doc.unknown"), None);
366
367 assert_eq!(registry.detect_format_from_filename("doc"), None);
369 }
370
371 #[test]
372 fn test_detect_format_case_sensitive() {
373 let registry = FormatRegistry::with_defaults();
374
375 assert_eq!(
377 registry.detect_format_from_filename("doc.lex"),
378 Some("lex".to_string())
379 );
380 }
383}