Skip to main content

ass_editor/formats/
registry.rs

1//! Runtime registry for available subtitle formats.
2//!
3//! Provides [`FormatRegistry`], which tracks registered importers, exporters,
4//! and combined formats and dispatches import/export by file extension.
5
6use super::{Format, FormatExporter, FormatImporter, FormatOptions, FormatResult};
7use crate::core::{EditorDocument, EditorError};
8use std::collections::HashMap;
9use std::path::Path;
10
11/// Registry for managing available formats
12#[derive(Debug, Default)]
13pub struct FormatRegistry {
14    pub(super) importers: HashMap<String, Box<dyn FormatImporter>>,
15    pub(super) exporters: HashMap<String, Box<dyn FormatExporter>>,
16    pub(super) formats: HashMap<String, Box<dyn Format>>,
17}
18
19impl FormatRegistry {
20    /// Create a new format registry
21    pub fn new() -> Self {
22        Self::default()
23    }
24
25    /// Register a format that supports both import and export
26    pub fn register_format(&mut self, format: Box<dyn Format>) {
27        let name = format.name().to_string();
28        self.formats.insert(name, format);
29    }
30
31    /// Register an importer
32    pub fn register_importer(&mut self, importer: Box<dyn FormatImporter>) {
33        let name = importer.format_info().name.clone();
34        self.importers.insert(name, importer);
35    }
36
37    /// Register an exporter
38    pub fn register_exporter(&mut self, exporter: Box<dyn FormatExporter>) {
39        let name = exporter.format_info().name.clone();
40        self.exporters.insert(name, exporter);
41    }
42
43    /// Find an importer for the given file extension
44    pub fn find_importer(&self, extension: &str) -> Option<&dyn FormatImporter> {
45        // Check combined formats first
46        for format in self.formats.values() {
47            if format.can_import(extension) {
48                return Some(format.as_importer());
49            }
50        }
51
52        // Check dedicated importers
53        for importer in self.importers.values() {
54            if importer.can_import(extension) {
55                return Some(importer.as_ref());
56            }
57        }
58
59        None
60    }
61
62    /// Find an exporter for the given file extension
63    pub fn find_exporter(&self, extension: &str) -> Option<&dyn FormatExporter> {
64        // Check combined formats first
65        for format in self.formats.values() {
66            if format.can_export(extension) {
67                return Some(format.as_exporter());
68            }
69        }
70
71        // Check dedicated exporters
72        for exporter in self.exporters.values() {
73            if exporter.can_export(extension) {
74                return Some(exporter.as_ref());
75            }
76        }
77
78        None
79    }
80
81    /// Get all supported import extensions
82    pub fn supported_import_extensions(&self) -> Vec<String> {
83        let mut extensions = Vec::new();
84
85        for format in self.formats.values() {
86            extensions.extend(
87                FormatImporter::format_info(format.as_ref())
88                    .extensions
89                    .clone(),
90            );
91        }
92
93        for importer in self.importers.values() {
94            extensions.extend(importer.format_info().extensions.clone());
95        }
96
97        extensions.sort();
98        extensions.dedup();
99        extensions
100    }
101
102    /// Get all supported export extensions
103    pub fn supported_export_extensions(&self) -> Vec<String> {
104        let mut extensions = Vec::new();
105
106        for format in self.formats.values() {
107            extensions.extend(
108                FormatExporter::format_info(format.as_ref())
109                    .extensions
110                    .clone(),
111            );
112        }
113
114        for exporter in self.exporters.values() {
115            extensions.extend(exporter.format_info().extensions.clone());
116        }
117
118        extensions.sort();
119        extensions.dedup();
120        extensions
121    }
122
123    /// Import a file using the appropriate format
124    pub fn import_file(
125        &self,
126        path: &Path,
127        options: Option<&FormatOptions>,
128    ) -> Result<(EditorDocument, FormatResult), EditorError> {
129        let extension = path
130            .extension()
131            .and_then(|ext| ext.to_str())
132            .ok_or_else(|| EditorError::InvalidFormat("No file extension found".to_string()))?;
133
134        let importer = self
135            .find_importer(extension)
136            .ok_or_else(|| EditorError::UnsupportedFormat(extension.to_string()))?;
137
138        let default_options = FormatOptions::default();
139        let options = options.unwrap_or(&default_options);
140        importer.import_from_path(path, options)
141    }
142
143    /// Export a document to a file using the appropriate format
144    pub fn export_file(
145        &self,
146        document: &EditorDocument,
147        path: &Path,
148        options: Option<&FormatOptions>,
149    ) -> Result<FormatResult, EditorError> {
150        let extension = path
151            .extension()
152            .and_then(|ext| ext.to_str())
153            .ok_or_else(|| EditorError::InvalidFormat("No file extension found".to_string()))?;
154
155        let exporter = self
156            .find_exporter(extension)
157            .ok_or_else(|| EditorError::UnsupportedFormat(extension.to_string()))?;
158
159        let default_options = FormatOptions::default();
160        let options = options.unwrap_or(&default_options);
161        exporter.export_to_path(document, path, options)
162    }
163}