Skip to main content

ass_editor/formats/
traits.rs

1//! Import/export trait definitions for subtitle formats.
2//!
3//! Defines [`FormatImporter`], [`FormatExporter`], and the combined [`Format`]
4//! trait used to read and write subtitle documents.
5
6use super::{FormatInfo, FormatOptions, FormatResult};
7use crate::core::{EditorDocument, EditorError};
8use std::fmt;
9use std::io::{Read, Write};
10use std::path::Path;
11
12/// Trait for importing subtitle files into EditorDocument
13pub trait FormatImporter: fmt::Debug + Send + Sync {
14    /// Get information about this format
15    fn format_info(&self) -> &FormatInfo;
16
17    /// Check if this importer can handle the given file extension
18    fn can_import(&self, extension: &str) -> bool {
19        self.format_info()
20            .extensions
21            .iter()
22            .any(|ext| ext.eq_ignore_ascii_case(extension))
23    }
24
25    /// Import from a reader with the given options
26    fn import_from_reader(
27        &self,
28        reader: &mut dyn Read,
29        options: &FormatOptions,
30    ) -> Result<(EditorDocument, FormatResult), EditorError>;
31
32    /// Import from a file path
33    fn import_from_path(
34        &self,
35        path: &Path,
36        options: &FormatOptions,
37    ) -> Result<(EditorDocument, FormatResult), EditorError> {
38        let mut file = std::fs::File::open(path)
39            .map_err(|e| EditorError::IoError(format!("Failed to open file: {e}")))?;
40        self.import_from_reader(&mut file, options)
41    }
42
43    /// Import from a string
44    fn import_from_string(
45        &self,
46        content: &str,
47        options: &FormatOptions,
48    ) -> Result<(EditorDocument, FormatResult), EditorError> {
49        let mut cursor = std::io::Cursor::new(content.as_bytes());
50        self.import_from_reader(&mut cursor, options)
51    }
52}
53
54/// Trait for exporting EditorDocument to subtitle files
55pub trait FormatExporter: fmt::Debug + Send + Sync {
56    /// Get information about this format
57    fn format_info(&self) -> &FormatInfo;
58
59    /// Check if this exporter can handle the given file extension
60    fn can_export(&self, extension: &str) -> bool {
61        self.format_info()
62            .extensions
63            .iter()
64            .any(|ext| ext.eq_ignore_ascii_case(extension))
65    }
66
67    /// Export to a writer with the given options
68    fn export_to_writer(
69        &self,
70        document: &EditorDocument,
71        writer: &mut dyn Write,
72        options: &FormatOptions,
73    ) -> Result<FormatResult, EditorError>;
74
75    /// Export to a file path
76    fn export_to_path(
77        &self,
78        document: &EditorDocument,
79        path: &Path,
80        options: &FormatOptions,
81    ) -> Result<FormatResult, EditorError> {
82        let mut file = std::fs::File::create(path)
83            .map_err(|e| EditorError::IoError(format!("Failed to create file: {e}")))?;
84        self.export_to_writer(document, &mut file, options)
85    }
86
87    /// Export to a string
88    fn export_to_string(
89        &self,
90        document: &EditorDocument,
91        options: &FormatOptions,
92    ) -> Result<(String, FormatResult), EditorError> {
93        let mut buffer = Vec::new();
94        let result = self.export_to_writer(document, &mut buffer, options)?;
95        let content = String::from_utf8(buffer)
96            .map_err(|e| EditorError::InvalidFormat(format!("Invalid UTF-8 output: {e}")))?;
97        Ok((content, result))
98    }
99}
100
101/// Combined trait for formats that support both import and export
102pub trait Format: FormatImporter + FormatExporter {
103    /// Get the format name
104    fn name(&self) -> &str {
105        &FormatImporter::format_info(self).name
106    }
107
108    /// Check if this format supports the given file extension
109    fn supports_extension(&self, extension: &str) -> bool {
110        self.can_import(extension) || self.can_export(extension)
111    }
112
113    /// Get self as an importer (workaround for trait upcasting)
114    fn as_importer(&self) -> &dyn FormatImporter;
115
116    /// Get self as an exporter (workaround for trait upcasting)
117    fn as_exporter(&self) -> &dyn FormatExporter;
118}