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 registry.register(crate::formats::pdf::PdfFormat::default());
139 registry.register(crate::formats::png::PngFormat::default());
140 registry.register(crate::formats::markdown::MarkdownFormat);
141 registry.register(crate::formats::tag::TagFormat);
142 registry.register(crate::formats::treeviz::TreevizFormat);
143 registry.register(crate::formats::linetreeviz::LinetreevizFormat);
144
145 registry
146 }
147}
148
149impl Default for FormatRegistry {
150 fn default() -> Self {
151 Self::with_defaults()
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use crate::format::Format;
159 use lex_core::lex::ast::{ContentItem, Document, Paragraph};
160
161 struct TestFormat;
163 impl Format for TestFormat {
164 fn name(&self) -> &str {
165 "test"
166 }
167 fn description(&self) -> &str {
168 "Test format"
169 }
170 fn supports_parsing(&self) -> bool {
171 true
172 }
173 fn supports_serialization(&self) -> bool {
174 true
175 }
176 fn parse(&self, _source: &str) -> Result<Document, FormatError> {
177 Ok(Document::with_content(vec![ContentItem::Paragraph(
178 Paragraph::from_line("test".to_string()),
179 )]))
180 }
181 fn serialize(&self, _doc: &Document) -> Result<String, FormatError> {
182 Ok("test output".to_string())
183 }
184 }
185
186 #[test]
187 fn test_registry_creation() {
188 let registry = FormatRegistry::new();
189 assert_eq!(registry.formats.len(), 0);
190 }
191
192 #[test]
193 fn test_registry_register() {
194 let mut registry = FormatRegistry::new();
195 registry.register(TestFormat);
196
197 assert!(registry.has("test"));
198 assert_eq!(registry.list_formats(), vec!["test"]);
199 }
200
201 #[test]
202 fn test_registry_get() {
203 let mut registry = FormatRegistry::new();
204 registry.register(TestFormat);
205
206 let format = registry.get("test");
207 assert!(format.is_ok());
208 assert_eq!(format.unwrap().name(), "test");
209 }
210
211 #[test]
212 fn test_registry_get_nonexistent() {
213 let registry = FormatRegistry::new();
214 let result = registry.get("nonexistent");
215 assert!(result.is_err());
216 }
217
218 #[test]
219 fn test_registry_has() {
220 let mut registry = FormatRegistry::new();
221 registry.register(TestFormat);
222
223 assert!(registry.has("test"));
224 assert!(!registry.has("nonexistent"));
225 }
226
227 #[test]
228 fn test_registry_parse() {
229 let mut registry = FormatRegistry::new();
230 registry.register(TestFormat);
231
232 let result = registry.parse("input", "test");
233 assert!(result.is_ok());
234 }
235
236 #[test]
237 fn test_registry_parse_not_found() {
238 let registry = FormatRegistry::new();
239
240 let result = registry.parse("input", "nonexistent");
241 assert!(result.is_err());
242 match result.unwrap_err() {
243 FormatError::FormatNotFound(name) => assert_eq!(name, "nonexistent"),
244 _ => panic!("Expected FormatNotFound error"),
245 }
246 }
247
248 #[test]
249 fn test_registry_serialize() {
250 let mut registry = FormatRegistry::new();
251 registry.register(TestFormat);
252
253 let doc = Document::with_content(vec![ContentItem::Paragraph(Paragraph::from_line(
254 "Hello".to_string(),
255 ))]);
256
257 let result = registry.serialize(&doc, "test");
258 assert!(result.is_ok());
259 assert_eq!(result.unwrap(), "test output");
260 }
261
262 #[test]
263 fn test_registry_serialize_not_found() {
264 let registry = FormatRegistry::new();
265 let doc = Document::with_content(vec![]);
266
267 let result = registry.serialize(&doc, "nonexistent");
268 assert!(result.is_err());
269 match result.unwrap_err() {
270 FormatError::FormatNotFound(name) => assert_eq!(name, "nonexistent"),
271 _ => panic!("Expected FormatNotFound error"),
272 }
273 }
274
275 #[test]
276 fn test_registry_serialize_with_options_default_behavior() {
277 let mut registry = FormatRegistry::new();
278 registry.register(TestFormat);
279
280 let doc = Document::with_content(vec![ContentItem::Paragraph(Paragraph::from_line(
281 "Hello".to_string(),
282 ))]);
283 let mut options = HashMap::new();
284 options.insert("unused".to_string(), "true".to_string());
285
286 let result = registry.serialize_with_options(&doc, "test", &options);
287 assert!(result.is_err());
288 }
289
290 #[test]
291 fn test_registry_list_formats() {
292 let mut registry = FormatRegistry::new();
293 registry.register(TestFormat);
294
295 let formats = registry.list_formats();
296 assert_eq!(formats.len(), 1);
297 assert_eq!(formats[0], "test");
298 }
299
300 #[test]
301 fn test_registry_with_defaults() {
302 let registry = FormatRegistry::with_defaults();
303 assert!(registry.has("lex"));
304 assert!(registry.has("markdown"));
305 assert!(registry.has("tag"));
306 assert!(registry.has("treeviz"));
307 }
308
309 #[test]
310 fn test_registry_default_trait() {
311 let registry = FormatRegistry::default();
312 assert!(registry.has("lex"));
313 assert!(registry.has("markdown"));
314 assert!(registry.has("tag"));
315 assert!(registry.has("treeviz"));
316 }
317
318 #[test]
319 fn test_registry_replace_format() {
320 let mut registry = FormatRegistry::new();
321 registry.register(TestFormat);
322 registry.register(TestFormat); assert_eq!(registry.list_formats().len(), 1);
325 }
326
327 #[test]
328 fn test_detect_format_from_filename() {
329 let registry = FormatRegistry::with_defaults();
330
331 assert_eq!(
333 registry.detect_format_from_filename("doc.lex"),
334 Some("lex".to_string())
335 );
336 assert_eq!(
337 registry.detect_format_from_filename("/path/to/file.lex"),
338 Some("lex".to_string())
339 );
340
341 assert_eq!(
343 registry.detect_format_from_filename("doc.tag"),
344 Some("tag".to_string())
345 );
346 assert_eq!(
347 registry.detect_format_from_filename("doc.xml"),
348 Some("tag".to_string())
349 );
350
351 assert_eq!(
353 registry.detect_format_from_filename("doc.tree"),
354 Some("treeviz".to_string())
355 );
356 assert_eq!(
357 registry.detect_format_from_filename("doc.treeviz"),
358 Some("treeviz".to_string())
359 );
360
361 assert_eq!(registry.detect_format_from_filename("doc.unknown"), None);
363
364 assert_eq!(registry.detect_format_from_filename("doc"), None);
366 }
367
368 #[test]
369 fn test_detect_format_case_sensitive() {
370 let registry = FormatRegistry::with_defaults();
371
372 assert_eq!(
374 registry.detect_format_from_filename("doc.lex"),
375 Some("lex".to_string())
376 );
377 }
380}