ass_editor/formats/ass/
format.rs1use crate::core::{EditorDocument, EditorError};
7use crate::formats::{
8 Format, FormatExporter, FormatImporter, FormatInfo, FormatOptions, FormatResult,
9};
10use ass_core::parser::Script;
11use std::io::{Read, Write};
12
13#[derive(Debug)]
15pub struct AssFormat {
16 info: FormatInfo,
17}
18
19impl AssFormat {
20 pub fn new() -> Self {
22 Self {
23 info: FormatInfo {
24 name: "ASS".to_string(),
25 extensions: vec!["ass".to_string()],
26 mime_type: "text/x-ass".to_string(),
27 description: "Advanced SubStation Alpha subtitle format".to_string(),
28 supports_styling: true,
29 supports_positioning: true,
30 },
31 }
32 }
33}
34
35impl Default for AssFormat {
36 fn default() -> Self {
37 Self::new()
38 }
39}
40
41impl FormatImporter for AssFormat {
42 fn format_info(&self) -> &FormatInfo {
43 &self.info
44 }
45
46 fn import_from_reader(
47 &self,
48 reader: &mut dyn Read,
49 _options: &FormatOptions,
50 ) -> Result<(EditorDocument, FormatResult), EditorError> {
51 let mut content = String::new();
53 reader
54 .read_to_string(&mut content)
55 .map_err(|e| EditorError::IoError(format!("Failed to read content: {e}")))?;
56
57 let script = Script::parse(&content)?;
59
60 let line_count = content.lines().count();
62
63 let document = EditorDocument::from_content(&content)?;
65
66 let mut result = FormatResult::success(line_count);
68
69 if let Some(ass_core::parser::ast::Section::ScriptInfo(script_info)) =
71 script.find_section(ass_core::parser::ast::SectionType::ScriptInfo)
72 {
73 if let Some(title) = script_info.get_field("Title") {
74 result = result.with_metadata("title".to_string(), title.to_string());
75 }
76 if let Some(script_type) = script_info.get_field("ScriptType") {
77 result = result.with_metadata("script_type".to_string(), script_type.to_string());
78 }
79 }
80
81 let section_count = script.sections().len();
83
84 result = result.with_metadata("sections".to_string(), section_count.to_string());
85
86 Ok((document, result))
87 }
88}
89
90impl FormatExporter for AssFormat {
91 fn format_info(&self) -> &FormatInfo {
92 &self.info
93 }
94
95 fn export_to_writer(
96 &self,
97 document: &EditorDocument,
98 writer: &mut dyn Write,
99 options: &FormatOptions,
100 ) -> Result<FormatResult, EditorError> {
101 let content = if options.preserve_formatting {
102 document.text()
104 } else {
105 document.parse_script_with(|script| script.to_ass_string())?
107 };
108
109 let line_count = content.lines().count();
111
112 let bytes = if options.encoding.eq_ignore_ascii_case("UTF-8") {
114 content.into_bytes()
115 } else {
116 let mut warnings = Vec::new();
119 if !options.encoding.eq_ignore_ascii_case("UTF-8") {
120 warnings.push(format!(
121 "Encoding '{}' not supported, using UTF-8 instead",
122 options.encoding
123 ));
124 }
125
126 let result = FormatResult::success(line_count).with_warnings(warnings);
127
128 writer
129 .write_all(&content.into_bytes())
130 .map_err(|e| EditorError::IoError(format!("Failed to write content: {e}")))?;
131
132 return Ok(result);
133 };
134
135 writer
136 .write_all(&bytes)
137 .map_err(|e| EditorError::IoError(format!("Failed to write content: {e}")))?;
138
139 Ok(FormatResult::success(line_count))
140 }
141}
142
143impl Format for AssFormat {
144 fn as_importer(&self) -> &dyn FormatImporter {
145 self
146 }
147
148 fn as_exporter(&self) -> &dyn FormatExporter {
149 self
150 }
151}