ass_editor/extensions/
registry_integration.rs

1//! Integration with ass-core's ExtensionRegistry
2//!
3//! This module provides the glue between ass-editor's extension system and
4//! ass-core's plugin system, allowing editor extensions to register tag handlers
5//! and section processors that will be used during ASS parsing.
6
7use crate::core::{EditorError, Result};
8use crate::extensions::EditorExtension;
9use ass_core::plugin::{ExtensionRegistry, SectionProcessor, SectionResult, TagHandler, TagResult};
10
11#[cfg(not(feature = "std"))]
12use alloc::{
13    boxed::Box,
14    format,
15    string::{String, ToString},
16    vec::Vec,
17};
18
19/// Wrapper that connects editor extensions to ass-core's ExtensionRegistry
20pub struct RegistryIntegration {
21    /// The ass-core extension registry
22    registry: ExtensionRegistry,
23    /// Editor extensions that provide tag handlers
24    tag_providers: Vec<Box<dyn EditorExtension>>,
25    /// Editor extensions that provide section processors
26    section_providers: Vec<Box<dyn EditorExtension>>,
27}
28
29impl core::fmt::Debug for RegistryIntegration {
30    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
31        f.debug_struct("RegistryIntegration")
32            .field("registry", &self.registry)
33            .field(
34                "tag_providers",
35                &format!("{} providers", self.tag_providers.len()),
36            )
37            .field(
38                "section_providers",
39                &format!("{} providers", self.section_providers.len()),
40            )
41            .finish()
42    }
43}
44
45impl RegistryIntegration {
46    /// Create a new registry integration
47    pub fn new() -> Self {
48        Self {
49            registry: ExtensionRegistry::new(),
50            tag_providers: Vec::new(),
51            section_providers: Vec::new(),
52        }
53    }
54
55    /// Register all built-in tag handlers from ass-core
56    pub fn register_builtin_handlers(&mut self) -> Result<()> {
57        use ass_core::plugin::tags::*;
58
59        // Register formatting handlers
60        for handler in formatting::create_formatting_handlers() {
61            self.registry.register_tag_handler(handler).map_err(|e| {
62                EditorError::ExtensionError {
63                    extension: "builtin".to_string(),
64                    message: format!("Failed to register formatting handler: {e}"),
65                }
66            })?;
67        }
68
69        // Register special character handlers
70        for handler in special::create_special_handlers() {
71            self.registry.register_tag_handler(handler).map_err(|e| {
72                EditorError::ExtensionError {
73                    extension: "builtin".to_string(),
74                    message: format!("Failed to register special handler: {e}"),
75                }
76            })?;
77        }
78
79        // Register font handlers
80        for handler in font::create_font_handlers() {
81            self.registry.register_tag_handler(handler).map_err(|e| {
82                EditorError::ExtensionError {
83                    extension: "builtin".to_string(),
84                    message: format!("Failed to register font handler: {e}"),
85                }
86            })?;
87        }
88
89        // Register advanced handlers
90        for handler in advanced::create_advanced_handlers() {
91            self.registry.register_tag_handler(handler).map_err(|e| {
92                EditorError::ExtensionError {
93                    extension: "builtin".to_string(),
94                    message: format!("Failed to register advanced handler: {e}"),
95                }
96            })?;
97        }
98
99        // Register alignment handlers
100        for handler in alignment::create_alignment_handlers() {
101            self.registry.register_tag_handler(handler).map_err(|e| {
102                EditorError::ExtensionError {
103                    extension: "builtin".to_string(),
104                    message: format!("Failed to register alignment handler: {e}"),
105                }
106            })?;
107        }
108
109        // Register karaoke handlers
110        for handler in karaoke::create_karaoke_handlers() {
111            self.registry.register_tag_handler(handler).map_err(|e| {
112                EditorError::ExtensionError {
113                    extension: "builtin".to_string(),
114                    message: format!("Failed to register karaoke handler: {e}"),
115                }
116            })?;
117        }
118
119        // Register position handlers
120        for handler in position::create_position_handlers() {
121            self.registry.register_tag_handler(handler).map_err(|e| {
122                EditorError::ExtensionError {
123                    extension: "builtin".to_string(),
124                    message: format!("Failed to register position handler: {e}"),
125                }
126            })?;
127        }
128
129        // Register color handlers
130        for handler in color::create_color_handlers() {
131            self.registry.register_tag_handler(handler).map_err(|e| {
132                EditorError::ExtensionError {
133                    extension: "builtin".to_string(),
134                    message: format!("Failed to register color handler: {e}"),
135                }
136            })?;
137        }
138
139        // Register transform handlers
140        for handler in transform::create_transform_handlers() {
141            self.registry.register_tag_handler(handler).map_err(|e| {
142                EditorError::ExtensionError {
143                    extension: "builtin".to_string(),
144                    message: format!("Failed to register transform handler: {e}"),
145                }
146            })?;
147        }
148
149        // Register animation handlers
150        for handler in animation::create_animation_handlers() {
151            self.registry.register_tag_handler(handler).map_err(|e| {
152                EditorError::ExtensionError {
153                    extension: "builtin".to_string(),
154                    message: format!("Failed to register animation handler: {e}"),
155                }
156            })?;
157        }
158
159        // Register clipping handlers
160        for handler in clipping::create_clipping_handlers() {
161            self.registry.register_tag_handler(handler).map_err(|e| {
162                EditorError::ExtensionError {
163                    extension: "builtin".to_string(),
164                    message: format!("Failed to register clipping handler: {e}"),
165                }
166            })?;
167        }
168
169        // Register misc handlers
170        for handler in misc::create_misc_handlers() {
171            self.registry.register_tag_handler(handler).map_err(|e| {
172                EditorError::ExtensionError {
173                    extension: "builtin".to_string(),
174                    message: format!("Failed to register misc handler: {e}"),
175                }
176            })?;
177        }
178
179        Ok(())
180    }
181
182    /// Register built-in section processors from ass-core
183    pub fn register_builtin_sections(&mut self) -> Result<()> {
184        use ass_core::plugin::sections::*;
185
186        // Register Aegisub section processor
187        self.registry
188            .register_section_processor(Box::new(aegisub::AegisubProjectProcessor))
189            .map_err(|e| EditorError::ExtensionError {
190                extension: "builtin".to_string(),
191                message: format!("Failed to register Aegisub section processor: {e}"),
192            })?;
193
194        Ok(())
195    }
196
197    /// Get the underlying ExtensionRegistry for use in parsing
198    pub fn registry(&self) -> &ExtensionRegistry {
199        &self.registry
200    }
201
202    /// Get mutable access to the registry
203    pub fn registry_mut(&mut self) -> &mut ExtensionRegistry {
204        &mut self.registry
205    }
206
207    /// Register a custom tag handler from an editor extension
208    pub fn register_custom_tag_handler(
209        &mut self,
210        extension_name: String,
211        handler: Box<dyn TagHandler>,
212    ) -> Result<()> {
213        self.registry
214            .register_tag_handler(handler)
215            .map_err(|e| EditorError::ExtensionError {
216                extension: extension_name,
217                message: format!("Failed to register tag handler: {e}"),
218            })
219    }
220
221    /// Register a custom section processor from an editor extension
222    pub fn register_custom_section_processor(
223        &mut self,
224        extension_name: String,
225        processor: Box<dyn SectionProcessor>,
226    ) -> Result<()> {
227        self.registry
228            .register_section_processor(processor)
229            .map_err(|e| EditorError::ExtensionError {
230                extension: extension_name,
231                message: format!("Failed to register section processor: {e}"),
232            })
233    }
234}
235
236/// Adapter that allows editor extensions to provide tag handlers
237#[allow(dead_code)]
238pub struct EditorTagHandlerAdapter {
239    extension_name: String,
240    tag_name: String,
241    extension: Box<dyn EditorExtension>,
242}
243
244impl EditorTagHandlerAdapter {
245    /// Create a new tag handler adapter
246    pub fn new(
247        extension_name: String,
248        tag_name: String,
249        extension: Box<dyn EditorExtension>,
250    ) -> Self {
251        Self {
252            extension_name,
253            tag_name,
254            extension,
255        }
256    }
257}
258
259impl TagHandler for EditorTagHandlerAdapter {
260    fn name(&self) -> &'static str {
261        // This is a limitation - we need to leak the string to get a 'static lifetime
262        Box::leak(self.tag_name.clone().into_boxed_str())
263    }
264
265    fn process(&self, _args: &str) -> TagResult {
266        // Extensions process tags through their command system
267        // This is a simplified implementation
268        TagResult::Processed
269    }
270
271    fn validate(&self, args: &str) -> bool {
272        !args.is_empty()
273    }
274}
275
276/// Adapter that allows editor extensions to provide section processors
277#[allow(dead_code)]
278pub struct EditorSectionProcessorAdapter {
279    extension_name: String,
280    section_name: String,
281    extension: Box<dyn EditorExtension>,
282}
283
284impl EditorSectionProcessorAdapter {
285    /// Create a new section processor adapter
286    pub fn new(
287        extension_name: String,
288        section_name: String,
289        extension: Box<dyn EditorExtension>,
290    ) -> Self {
291        Self {
292            extension_name,
293            section_name,
294            extension,
295        }
296    }
297}
298
299impl SectionProcessor for EditorSectionProcessorAdapter {
300    fn name(&self) -> &'static str {
301        // This is a limitation - we need to leak the string to get a 'static lifetime
302        Box::leak(self.section_name.clone().into_boxed_str())
303    }
304
305    fn process(&self, _header: &str, _lines: &[&str]) -> SectionResult {
306        // Extensions process sections through their command system
307        // This is a simplified implementation
308        SectionResult::Processed
309    }
310
311    fn validate(&self, header: &str, lines: &[&str]) -> bool {
312        !header.is_empty() && !lines.is_empty()
313    }
314}
315
316impl Default for RegistryIntegration {
317    fn default() -> Self {
318        Self::new()
319    }
320}
321
322#[cfg(test)]
323mod tests {
324    use super::*;
325    #[cfg(not(feature = "std"))]
326    use alloc::string::ToString;
327
328    #[test]
329    fn test_registry_integration_creation() {
330        let integration = RegistryIntegration::new();
331        assert!(integration.tag_providers.is_empty());
332        assert!(integration.section_providers.is_empty());
333    }
334
335    #[test]
336    fn test_register_builtin_handlers() {
337        let mut integration = RegistryIntegration::new();
338
339        // Should successfully register all built-in handlers
340        assert!(integration.register_builtin_handlers().is_ok());
341    }
342
343    #[test]
344    fn test_register_builtin_sections() {
345        let mut integration = RegistryIntegration::new();
346
347        // Should successfully register built-in section processors
348        assert!(integration.register_builtin_sections().is_ok());
349    }
350
351    #[test]
352    fn test_custom_tag_handler_registration() {
353        use ass_core::plugin::{TagHandler, TagResult};
354
355        struct TestTagHandler;
356        impl TagHandler for TestTagHandler {
357            fn name(&self) -> &'static str {
358                "test"
359            }
360
361            fn process(&self, _args: &str) -> TagResult {
362                TagResult::Processed
363            }
364
365            fn validate(&self, _args: &str) -> bool {
366                true
367            }
368        }
369
370        let mut integration = RegistryIntegration::new();
371        let handler = Box::new(TestTagHandler);
372
373        assert!(integration
374            .register_custom_tag_handler("test-extension".to_string(), handler)
375            .is_ok());
376    }
377
378    #[test]
379    fn test_custom_section_processor_registration() {
380        use ass_core::plugin::{SectionProcessor, SectionResult};
381
382        struct TestSectionProcessor;
383        impl SectionProcessor for TestSectionProcessor {
384            fn name(&self) -> &'static str {
385                "TestSection"
386            }
387
388            fn process(&self, _header: &str, _lines: &[&str]) -> SectionResult {
389                SectionResult::Processed
390            }
391
392            fn validate(&self, _header: &str, _lines: &[&str]) -> bool {
393                true
394            }
395        }
396
397        let mut integration = RegistryIntegration::new();
398        let processor = Box::new(TestSectionProcessor);
399
400        assert!(integration
401            .register_custom_section_processor("test-extension".to_string(), processor)
402            .is_ok());
403    }
404
405    #[test]
406    fn test_registry_access() {
407        let integration = RegistryIntegration::new();
408
409        // Should be able to access the registry
410        let _registry = integration.registry();
411
412        // Mutable access
413        let mut integration = RegistryIntegration::new();
414        let _registry_mut = integration.registry_mut();
415    }
416
417    #[test]
418    fn test_full_integration() {
419        let mut integration = RegistryIntegration::new();
420
421        // Register all built-ins
422        assert!(integration.register_builtin_handlers().is_ok());
423        assert!(integration.register_builtin_sections().is_ok());
424
425        // The registry should now have many handlers registered
426        // We can't easily test the exact count, but we can verify it worked
427        let registry = integration.registry();
428
429        // Use the registry to parse some ASS content with tags
430        let test_content = "[Script Info]\nTitle: Test\n\n[Events]\nDialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,,{\\b1}Bold{\\b0} text";
431
432        // Parse with the registry
433        let result = ass_core::parser::Script::builder()
434            .with_registry(registry)
435            .parse(test_content);
436
437        assert!(result.is_ok());
438    }
439}