Skip to main content

ass_core/plugin/
registry.rs

1//! Central registry for ASS format extensions.
2//!
3//! Provides [`ExtensionRegistry`], which stores and dispatches registered
4//! [`TagHandler`] and [`SectionProcessor`] implementations during parsing.
5
6use super::{PluginError, Result, SectionProcessor, SectionResult, TagHandler, TagResult};
7use alloc::{
8    boxed::Box,
9    string::{String, ToString},
10    vec::Vec,
11};
12use core::fmt;
13
14#[cfg(feature = "std")]
15use std::collections::HashMap;
16
17#[cfg(not(feature = "std"))]
18use hashbrown::HashMap;
19
20/// Central registry for all ASS format extensions
21///
22/// Manages registration and lookup of tag handlers and section processors.
23/// Optimized for fast lookup during parsing with minimal memory overhead.
24pub struct ExtensionRegistry {
25    /// Registered tag handlers indexed by tag name
26    tag_handlers: HashMap<String, Box<dyn TagHandler>>,
27    /// Registered section processors indexed by section name
28    section_processors: HashMap<String, Box<dyn SectionProcessor>>,
29}
30
31impl ExtensionRegistry {
32    /// Create a new empty extension registry
33    #[must_use]
34    pub fn new() -> Self {
35        Self {
36            tag_handlers: HashMap::new(),
37            section_processors: HashMap::new(),
38        }
39    }
40
41    /// Register a new tag handler
42    ///
43    /// # Arguments
44    /// * `handler` - Boxed tag handler implementation
45    ///
46    /// # Errors
47    /// Returns `PluginError::DuplicateHandler` if handler name already exists
48    pub fn register_tag_handler(&mut self, handler: Box<dyn TagHandler>) -> Result<()> {
49        let name = handler.name().to_string();
50
51        if self.tag_handlers.contains_key(&name) {
52            return Err(PluginError::DuplicateHandler(name));
53        }
54
55        self.tag_handlers.insert(name, handler);
56        Ok(())
57    }
58
59    /// Register a new section processor
60    ///
61    /// # Arguments
62    /// * `processor` - Boxed section processor implementation
63    ///
64    /// # Errors
65    /// Returns `PluginError::DuplicateHandler` if processor name already exists
66    pub fn register_section_processor(
67        &mut self,
68        processor: Box<dyn SectionProcessor>,
69    ) -> Result<()> {
70        let name = processor.name().to_string();
71
72        if self.section_processors.contains_key(&name) {
73            return Err(PluginError::DuplicateHandler(name));
74        }
75
76        self.section_processors.insert(name, processor);
77        Ok(())
78    }
79
80    /// Process a tag using registered handlers
81    ///
82    /// # Arguments
83    /// * `tag_name` - Name of the tag to process
84    /// * `args` - Tag arguments as string slice
85    ///
86    /// # Returns
87    /// * `Some(TagResult)` - If a handler was found and executed
88    /// * `None` - If no handler was registered for this tag
89    #[must_use]
90    pub fn process_tag(&self, tag_name: &str, args: &str) -> Option<TagResult> {
91        self.tag_handlers
92            .get(tag_name)
93            .map(|handler| handler.process(args))
94    }
95
96    /// Process a section using registered processors
97    ///
98    /// # Arguments
99    /// * `section_name` - Name of the section to process
100    /// * `header` - Section header line
101    /// * `lines` - All lines in the section
102    ///
103    /// # Returns
104    /// * `Some(SectionResult)` - If a processor was found and executed
105    /// * `None` - If no processor was registered for this section
106    #[must_use]
107    pub fn process_section(
108        &self,
109        section_name: &str,
110        header: &str,
111        lines: &[&str],
112    ) -> Option<SectionResult> {
113        self.section_processors
114            .get(section_name)
115            .map(|processor| processor.process(header, lines))
116    }
117
118    /// Get list of registered tag handler names
119    #[must_use]
120    pub fn tag_handler_names(&self) -> Vec<&str> {
121        self.tag_handlers.keys().map(String::as_str).collect()
122    }
123
124    /// Get list of registered section processor names
125    #[must_use]
126    pub fn section_processor_names(&self) -> Vec<&str> {
127        self.section_processors.keys().map(String::as_str).collect()
128    }
129
130    /// Check if a tag handler is registered
131    #[must_use]
132    pub fn has_tag_handler(&self, name: &str) -> bool {
133        self.tag_handlers.contains_key(name)
134    }
135
136    /// Check if a section processor is registered
137    #[must_use]
138    pub fn has_section_processor(&self, name: &str) -> bool {
139        self.section_processors.contains_key(name)
140    }
141
142    /// Remove a tag handler by name
143    ///
144    /// # Returns
145    /// * `Some(handler)` - If handler was found and removed
146    /// * `None` - If no handler with that name was registered
147    pub fn remove_tag_handler(&mut self, name: &str) -> Option<Box<dyn TagHandler>> {
148        self.tag_handlers.remove(name)
149    }
150
151    /// Remove a section processor by name
152    ///
153    /// # Returns
154    /// * `Some(processor)` - If processor was found and removed
155    /// * `None` - If no processor with that name was registered
156    pub fn remove_section_processor(&mut self, name: &str) -> Option<Box<dyn SectionProcessor>> {
157        self.section_processors.remove(name)
158    }
159
160    /// Clear all registered handlers and processors
161    pub fn clear(&mut self) {
162        self.tag_handlers.clear();
163        self.section_processors.clear();
164    }
165
166    /// Get total number of registered extensions
167    #[must_use]
168    pub fn extension_count(&self) -> usize {
169        self.tag_handlers.len() + self.section_processors.len()
170    }
171}
172
173impl Default for ExtensionRegistry {
174    fn default() -> Self {
175        Self::new()
176    }
177}
178
179impl fmt::Debug for ExtensionRegistry {
180    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181        f.debug_struct("ExtensionRegistry")
182            .field("tag_handlers", &self.tag_handler_names())
183            .field("section_processors", &self.section_processor_names())
184            .finish()
185    }
186}