Skip to main content

horon_engine/
extension.rs

1//! extension.rs - Pluggable Extension System for horon-engine
2//!
3//! Provides a comprehensive extension framework for the HTT system,
4//! enabling integration with various backends, databases, and specialized
5//! processing capabilities.
6//!
7//! ## Extension Categories
8//!
9//! - **Storage Adapters**: Connect HTT to databases (MySQL, PostgreSQL, MongoDB, SQLite)
10//! - **Persistence Providers**: Enable durable storage on disk or distributed systems
11//! - **Semantic Analyzers**: Extract meaning and structure from text or code
12//! - **Geometric Enhancers**: Discover and utilize hidden geometric patterns in data
13
14use std::any::Any;
15use std::collections::HashMap;
16use std::fmt::{self, Debug, Formatter};
17use std::sync::{Arc, RwLock};
18use super::tensor_network::CompressedNode;
19use super::tree_tensor::IntegrationError;
20
21/// Result type for extension operations.
22pub type ExtensionResult<T> = Result<T, ExtensionError>;
23
24/// Error type for extension operations.
25#[derive(Debug)]
26pub enum ExtensionError {
27    /// Storage-related errors
28    Storage(String),
29    /// Processing-related errors
30    Processing(String),
31    /// Configuration errors
32    Configuration(String),
33    /// Serialization errors
34    Serialization(String),
35    /// Backend-specific errors
36    Backend(String),
37    /// Unsupported operation
38    Unsupported(String),
39    /// Integration errors from the main system
40    Integration(IntegrationError),
41}
42
43impl From<IntegrationError> for ExtensionError {
44    fn from(err: IntegrationError) -> Self {
45        ExtensionError::Integration(err)
46    }
47}
48
49impl From<serde_json::Error> for ExtensionError {
50    fn from(err: serde_json::Error) -> Self {
51        ExtensionError::Serialization(err.to_string())
52    }
53}
54
55impl fmt::Display for ExtensionError {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        match self {
58            ExtensionError::Storage(msg) => write!(f, "Storage error: {}", msg),
59            ExtensionError::Processing(msg) => write!(f, "Processing error: {}", msg),
60            ExtensionError::Configuration(msg) => write!(f, "Configuration error: {}", msg),
61            ExtensionError::Serialization(msg) => write!(f, "Serialization error: {}", msg),
62            ExtensionError::Backend(msg) => write!(f, "Backend error: {}", msg),
63            ExtensionError::Unsupported(msg) => write!(f, "Unsupported operation: {}", msg),
64            ExtensionError::Integration(err) => write!(f, "Integration error: {}", err),
65        }
66    }
67}
68
69impl std::error::Error for ExtensionError {}
70
71/// Extension capability flags.
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub struct ExtensionCapabilities {
74    /// Can store data
75    pub can_store: bool,
76    /// Can retrieve data
77    pub can_retrieve: bool,
78    /// Can process nodes
79    pub can_process: bool,
80    /// Can enhance queries
81    pub can_enhance_queries: bool,
82    /// Can optimize structure
83    pub can_optimize: bool,
84    /// Can analyze semantic content
85    pub can_analyze_semantics: bool,
86}
87
88impl ExtensionCapabilities {
89    /// Create new capabilities with all flags disabled.
90    pub fn none() -> Self {
91        Self {
92            can_store: false,
93            can_retrieve: false,
94            can_process: false,
95            can_enhance_queries: false,
96            can_optimize: false,
97            can_analyze_semantics: false,
98        }
99    }
100
101    /// Create capabilities for a storage provider.
102    pub fn storage() -> Self {
103        Self {
104            can_store: true,
105            can_retrieve: true,
106            can_process: false,
107            can_enhance_queries: false,
108            can_optimize: false,
109            can_analyze_semantics: false,
110        }
111    }
112
113    /// Create capabilities for a processor extension.
114    pub fn processor() -> Self {
115        Self {
116            can_store: false,
117            can_retrieve: false,
118            can_process: true,
119            can_enhance_queries: true,
120            can_optimize: false,
121            can_analyze_semantics: false,
122        }
123    }
124
125    /// Create capabilities for a semantic analyzer.
126    pub fn semantic_analyzer() -> Self {
127        Self {
128            can_store: false,
129            can_retrieve: false,
130            can_process: true,
131            can_enhance_queries: true,
132            can_optimize: false,
133            can_analyze_semantics: true,
134        }
135    }
136
137    /// Create capabilities for an optimizer.
138    pub fn optimizer() -> Self {
139        Self {
140            can_store: false,
141            can_retrieve: false,
142            can_process: false,
143            can_enhance_queries: false,
144            can_optimize: true,
145            can_analyze_semantics: false,
146        }
147    }
148}
149
150/// Base extension interface for the HTT module.
151pub trait HTTExtensionBase: Send + Sync {
152    /// Get the extension name.
153    fn name(&self) -> &str;
154
155    /// Get the extension version.
156    fn version(&self) -> &str;
157
158    /// Get the extension description.
159    fn description(&self) -> &str;
160
161    /// Get the extension capabilities.
162    fn capabilities(&self) -> ExtensionCapabilities;
163
164    /// Get extension metadata.
165    fn metadata(&self) -> HashMap<String, String>;
166
167    /// Initialize the extension.
168    fn initialize(&mut self) -> ExtensionResult<()>;
169
170    /// Shut down the extension.
171    fn shutdown(&mut self) -> ExtensionResult<()>;
172
173    /// Check if the extension is compatible with the given data type.
174    fn is_compatible_with(&self, data_type: &str) -> bool;
175
176    /// Convert the extension to Any for downcasting.
177    fn as_any(&self) -> &dyn Any;
178
179    /// Convert the extension to mutable Any for downcasting.
180    fn as_any_mut(&mut self) -> &mut dyn Any;
181}
182
183/// Node processing extension interface.
184///
185/// Allows extensions to process CompressedNode instances,
186/// enhancing them with additional capabilities or extracting information.
187pub trait HTTExtension: HTTExtensionBase {
188    /// Process a node, potentially transforming it.
189    fn process_node(&self, node: &CompressedNode) -> ExtensionResult<CompressedNode>;
190
191    /// Process multiple nodes in batch.
192    fn process_nodes(&self, nodes: &[CompressedNode]) -> ExtensionResult<Vec<CompressedNode>> {
193        let mut results = Vec::with_capacity(nodes.len());
194        for node in nodes {
195            results.push(self.process_node(node)?);
196        }
197        Ok(results)
198    }
199
200    /// Enhance a query before it's executed.
201    fn enhance_query(
202        &self,
203        path: &str,
204        query_params: &HashMap<String, String>,
205    ) -> ExtensionResult<(String, HashMap<String, String>)>;
206
207    /// Extract information from a node.
208    fn extract_info(&self, node: &CompressedNode) -> ExtensionResult<HashMap<String, String>>;
209
210    /// Check if this extension can process the given node.
211    fn can_process(&self, node: &CompressedNode) -> bool;
212}
213
214/// Storage provider interface for HTT.
215///
216/// Allows HTT to be integrated with various storage backends.
217pub trait HTTStorageProvider: HTTExtensionBase {
218    /// Store data at the given path.
219    fn store(&mut self, path: &str, data: &[u8]) -> ExtensionResult<()>;
220
221    /// Retrieve data from the given path.
222    fn retrieve(&self, path: &str) -> ExtensionResult<Vec<u8>>;
223
224    /// Delete data at the given path.
225    fn delete(&mut self, path: &str) -> ExtensionResult<()>;
226
227    /// Check if data exists at the given path.
228    fn exists(&self, path: &str) -> ExtensionResult<bool>;
229
230    /// List paths matching the given prefix.
231    fn list(&self, prefix: &str) -> ExtensionResult<Vec<String>>;
232
233    /// Get metadata for the given path.
234    fn get_metadata(&self, path: &str) -> ExtensionResult<HashMap<String, String>>;
235
236    /// Set metadata for the given path.
237    fn set_metadata(&mut self, path: &str, key: &str, value: &str) -> ExtensionResult<()>;
238
239    /// Flush any pending changes to durable storage.
240    fn flush(&mut self) -> ExtensionResult<()>;
241
242    /// Begin a transaction.
243    fn begin_transaction(&mut self) -> ExtensionResult<()>;
244
245    /// Commit a transaction.
246    fn commit_transaction(&mut self) -> ExtensionResult<()>;
247
248    /// Rollback a transaction.
249    fn rollback_transaction(&mut self) -> ExtensionResult<()>;
250
251    /// Check if the provider supports transactions.
252    fn supports_transactions(&self) -> bool;
253
254    /// Check if the provider is available.
255    fn is_available(&self) -> bool;
256
257    /// Get provider statistics.
258    fn stats(&self) -> ExtensionResult<HashMap<String, String>>;
259}
260
261/// Extension manager for coordinating multiple extensions.
262pub struct HTTExtensionManager {
263    /// Registered extensions
264    extensions: HashMap<String, Box<dyn HTTExtensionBase>>,
265    /// Extension configurations
266    configs: HashMap<String, HashMap<String, String>>,
267    /// Extension dependency ordering
268    dependency_order: Vec<String>,
269    /// Extension capabilities cache
270    capabilities_cache: HashMap<String, ExtensionCapabilities>,
271}
272
273impl HTTExtensionManager {
274    /// Create a new extension manager.
275    pub fn new() -> Self {
276        Self {
277            extensions: HashMap::new(),
278            configs: HashMap::new(),
279            dependency_order: Vec::new(),
280            capabilities_cache: HashMap::new(),
281        }
282    }
283
284    /// Register an extension.
285    pub fn register_extension<E: HTTExtensionBase + 'static>(
286        &mut self,
287        extension: E,
288        config: HashMap<String, String>,
289    ) -> ExtensionResult<()> {
290        let name = extension.name().to_string();
291
292        self.capabilities_cache
293            .insert(name.clone(), extension.capabilities());
294        self.extensions.insert(name.clone(), Box::new(extension));
295        self.configs.insert(name.clone(), config);
296
297        if !self.dependency_order.contains(&name) {
298            self.dependency_order.push(name);
299        }
300
301        Ok(())
302    }
303
304    /// Get an extension by name.
305    pub fn get_extension(&self, name: &str) -> Option<&dyn HTTExtensionBase> {
306        self.extensions.get(name).map(|ext| ext.as_ref())
307    }
308
309    /// Get a mutable extension by name.
310    pub fn get_extension_mut(
311        &mut self,
312        name: &str,
313    ) -> Option<&mut (dyn HTTExtensionBase + 'static)> {
314        self.extensions.get_mut(name).map(|ext| &mut **ext)
315    }
316
317    /// Get an extension as a specific type.
318    pub fn get_extension_as<T: 'static>(&self, name: &str) -> Option<&T> {
319        self.get_extension(name)
320            .and_then(|ext| ext.as_any().downcast_ref::<T>())
321    }
322
323    /// Get a mutable extension as a specific type.
324    pub fn get_extension_mut_as<T: 'static>(&mut self, name: &str) -> Option<&mut T> {
325        self.get_extension_mut(name)
326            .and_then(|ext| ext.as_any_mut().downcast_mut::<T>())
327    }
328
329    /// Get all registered extension names.
330    pub fn extension_names(&self) -> Vec<String> {
331        self.extensions.keys().cloned().collect()
332    }
333
334    /// Get all extensions with specific capabilities.
335    pub fn extensions_with_capability(
336        &self,
337        capability: fn(&ExtensionCapabilities) -> bool,
338    ) -> Vec<&dyn HTTExtensionBase> {
339        self.extensions
340            .values()
341            .filter(|ext| capability(&ext.capabilities()))
342            .map(|ext| ext.as_ref())
343            .collect()
344    }
345
346    /// Initialize all extensions in dependency order.
347    pub fn initialize_all(&mut self) -> ExtensionResult<()> {
348        for name in &self.dependency_order {
349            if let Some(ext) = self.extensions.get_mut(name) {
350                ext.initialize()?;
351            }
352        }
353        Ok(())
354    }
355
356    /// Shut down all extensions in reverse dependency order.
357    pub fn shutdown_all(&mut self) -> ExtensionResult<()> {
358        for name in self.dependency_order.iter().rev() {
359            if let Some(ext) = self.extensions.get_mut(name) {
360                ext.shutdown()?;
361            }
362        }
363        Ok(())
364    }
365
366    /// Get configuration for an extension.
367    pub fn get_config(&self, name: &str) -> Option<&HashMap<String, String>> {
368        self.configs.get(name)
369    }
370
371    /// Set configuration for an extension.
372    pub fn set_config(
373        &mut self,
374        name: &str,
375        config: HashMap<String, String>,
376    ) -> ExtensionResult<()> {
377        if self.extensions.contains_key(name) {
378            self.configs.insert(name.to_string(), config);
379            Ok(())
380        } else {
381            Err(ExtensionError::Configuration(format!(
382                "Extension {} not found",
383                name
384            )))
385        }
386    }
387
388    /// Check if an extension is registered.
389    pub fn has_extension(&self, name: &str) -> bool {
390        self.extensions.contains_key(name)
391    }
392
393    /// Remove an extension.
394    pub fn remove_extension(&mut self, name: &str) -> ExtensionResult<()> {
395        if !self.extensions.contains_key(name) {
396            return Err(ExtensionError::Configuration(format!(
397                "Extension {} not found",
398                name
399            )));
400        }
401
402        if let Some(ext) = self.extensions.get_mut(name) {
403            ext.shutdown()?;
404        }
405
406        self.extensions.remove(name);
407        self.configs.remove(name);
408        self.capabilities_cache.remove(name);
409        self.dependency_order.retain(|n| n != name);
410
411        Ok(())
412    }
413}
414
415impl Debug for HTTExtensionManager {
416    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
417        f.debug_struct("HTTExtensionManager")
418            .field("extension_count", &self.extensions.len())
419            .field("extension_names", &self.extension_names())
420            .finish()
421    }
422}
423
424/// Extension registry for system-wide extension management.
425pub struct ExtensionRegistry {
426    /// The singleton manager instance
427    manager: Arc<RwLock<HTTExtensionManager>>,
428}
429
430impl ExtensionRegistry {
431    /// Get the global extension registry instance.
432    pub fn instance() -> Self {
433        use std::sync::OnceLock;
434        static MANAGER: OnceLock<Arc<RwLock<HTTExtensionManager>>> = OnceLock::new();
435
436        let manager = MANAGER.get_or_init(|| {
437            Arc::new(RwLock::new(HTTExtensionManager::new()))
438        });
439
440        Self {
441            manager: manager.clone(),
442        }
443    }
444
445    /// Get a read-only reference to the extension manager.
446    pub fn manager(
447        &self,
448    ) -> ExtensionResult<std::sync::RwLockReadGuard<'_, HTTExtensionManager>> {
449        self.manager.read().map_err(|e| {
450            ExtensionError::Configuration(format!("Failed to lock extension manager: {}", e))
451        })
452    }
453
454    /// Get a mutable reference to the extension manager.
455    pub fn manager_mut(
456        &self,
457    ) -> ExtensionResult<std::sync::RwLockWriteGuard<'_, HTTExtensionManager>> {
458        self.manager.write().map_err(|e| {
459            ExtensionError::Configuration(format!("Failed to lock extension manager: {}", e))
460        })
461    }
462}
463
464/// Simple file system storage provider implementation.
465pub struct FileSystemStorageProvider {
466    /// Root directory for storage
467    root_dir: String,
468    /// Extension name
469    name: String,
470    /// Extension version
471    version: String,
472    /// Extension description
473    description: String,
474    /// Extension metadata
475    ext_metadata: HashMap<String, String>,
476    /// In-memory metadata cache
477    metadata_cache: HashMap<String, HashMap<String, String>>,
478}
479
480impl FileSystemStorageProvider {
481    /// Create a new file system storage provider.
482    pub fn new(root_dir: String) -> Self {
483        Self {
484            root_dir,
485            name: "FileSystemStorageProvider".to_string(),
486            version: "1.0.0".to_string(),
487            description: "File system storage provider for HTT".to_string(),
488            ext_metadata: HashMap::new(),
489            metadata_cache: HashMap::new(),
490        }
491    }
492
493    fn path_to_fs_path(&self, path: &str) -> String {
494        let normalized_path = if path.starts_with('/') {
495            path.trim_start_matches('/')
496        } else {
497            path
498        };
499        format!("{}/{}", self.root_dir, normalized_path)
500    }
501
502    fn ensure_parent_dirs(&self, path: &str) -> ExtensionResult<()> {
503        let fs_path = self.path_to_fs_path(path);
504        if let Some(parent) = std::path::Path::new(&fs_path).parent() {
505            std::fs::create_dir_all(parent)
506                .map_err(|e| ExtensionError::Storage(format!("Failed to create directories: {}", e)))?;
507        }
508        Ok(())
509    }
510
511    /// Read and parse a path's metadata straight from disk, without touching
512    /// the cache. Returns an empty map when no metadata file exists. Pure
513    /// (`&self`) so callers holding only a shared reference can read without
514    /// cloning the whole provider.
515    fn read_metadata_from_disk(&self, path: &str) -> ExtensionResult<HashMap<String, String>> {
516        let metadata_path = format!("{}.metadata", self.path_to_fs_path(path));
517
518        if std::path::Path::new(&metadata_path).exists() {
519            let metadata_str = std::fs::read_to_string(&metadata_path)
520                .map_err(|e| ExtensionError::Storage(format!("Failed to read metadata: {}", e)))?;
521
522            serde_json::from_str(&metadata_str).map_err(|e| {
523                ExtensionError::Serialization(format!("Failed to parse metadata: {}", e))
524            })
525        } else {
526            Ok(HashMap::new())
527        }
528    }
529
530    fn load_metadata(&mut self, path: &str) -> ExtensionResult<HashMap<String, String>> {
531        let metadata = self.read_metadata_from_disk(path)?;
532        if !metadata.is_empty() {
533            self.metadata_cache
534                .insert(path.to_string(), metadata.clone());
535        }
536        Ok(metadata)
537    }
538
539    fn save_metadata(
540        &self,
541        path: &str,
542        metadata: &HashMap<String, String>,
543    ) -> ExtensionResult<()> {
544        let metadata_path = format!("{}.metadata", self.path_to_fs_path(path));
545        let metadata_str = serde_json::to_string(metadata).map_err(|e| {
546            ExtensionError::Serialization(format!("Failed to serialize metadata: {}", e))
547        })?;
548        self.ensure_parent_dirs(path)?;
549        std::fs::write(&metadata_path, metadata_str)
550            .map_err(|e| ExtensionError::Storage(format!("Failed to write metadata: {}", e)))?;
551        Ok(())
552    }
553}
554
555impl HTTExtensionBase for FileSystemStorageProvider {
556    fn name(&self) -> &str {
557        &self.name
558    }
559
560    fn version(&self) -> &str {
561        &self.version
562    }
563
564    fn description(&self) -> &str {
565        &self.description
566    }
567
568    fn capabilities(&self) -> ExtensionCapabilities {
569        ExtensionCapabilities::storage()
570    }
571
572    fn metadata(&self) -> HashMap<String, String> {
573        let mut meta = self.ext_metadata.clone();
574        meta.insert("root_dir".to_string(), self.root_dir.clone());
575        meta
576    }
577
578    fn initialize(&mut self) -> ExtensionResult<()> {
579        std::fs::create_dir_all(&self.root_dir)
580            .map_err(|e| ExtensionError::Storage(format!("Failed to create root directory: {}", e)))?;
581        Ok(())
582    }
583
584    fn shutdown(&mut self) -> ExtensionResult<()> {
585        Ok(())
586    }
587
588    fn is_compatible_with(&self, data_type: &str) -> bool {
589        data_type == "bytes" || data_type == "CompressedNode"
590    }
591
592    fn as_any(&self) -> &dyn Any {
593        self
594    }
595
596    fn as_any_mut(&mut self) -> &mut dyn Any {
597        self
598    }
599}
600
601impl HTTStorageProvider for FileSystemStorageProvider {
602    fn store(&mut self, path: &str, data: &[u8]) -> ExtensionResult<()> {
603        let fs_path = self.path_to_fs_path(path);
604        self.ensure_parent_dirs(path)?;
605        std::fs::write(&fs_path, data)
606            .map_err(|e| ExtensionError::Storage(format!("Failed to write file: {}", e)))?;
607        Ok(())
608    }
609
610    fn retrieve(&self, path: &str) -> ExtensionResult<Vec<u8>> {
611        let fs_path = self.path_to_fs_path(path);
612        std::fs::read(&fs_path)
613            .map_err(|e| ExtensionError::Storage(format!("Failed to read file: {}", e)))
614    }
615
616    fn delete(&mut self, path: &str) -> ExtensionResult<()> {
617        let fs_path = self.path_to_fs_path(path);
618        let metadata_path = format!("{}.metadata", fs_path);
619
620        self.metadata_cache.remove(path);
621
622        if std::path::Path::new(&metadata_path).exists() {
623            std::fs::remove_file(&metadata_path)
624                .map_err(|e| ExtensionError::Storage(format!("Failed to delete metadata file: {}", e)))?;
625        }
626
627        if std::path::Path::new(&fs_path).exists() {
628            std::fs::remove_file(&fs_path)
629                .map_err(|e| ExtensionError::Storage(format!("Failed to delete file: {}", e)))?;
630        }
631
632        Ok(())
633    }
634
635    fn exists(&self, path: &str) -> ExtensionResult<bool> {
636        let fs_path = self.path_to_fs_path(path);
637        Ok(std::path::Path::new(&fs_path).exists())
638    }
639
640    fn list(&self, prefix: &str) -> ExtensionResult<Vec<String>> {
641        let prefix_path = self.path_to_fs_path(prefix);
642        let prefix_dir = if std::path::Path::new(&prefix_path).is_dir() {
643            prefix_path.clone()
644        } else {
645            std::path::Path::new(&prefix_path)
646                .parent()
647                .map(|p| p.to_string_lossy().to_string())
648                .unwrap_or_else(|| self.root_dir.clone())
649        };
650
651        fn walk_dir(
652            dir: &std::path::Path,
653            prefix: &str,
654            root_dir: &str,
655        ) -> ExtensionResult<Vec<String>> {
656            let mut results = Vec::new();
657            if dir.exists() && dir.is_dir() {
658                for entry in std::fs::read_dir(dir)
659                    .map_err(|e| ExtensionError::Storage(format!("Failed to read directory: {}", e)))?
660                {
661                    let entry = entry.map_err(|e| {
662                        ExtensionError::Storage(format!("Failed to read entry: {}", e))
663                    })?;
664                    let path = entry.path();
665
666                    if path.is_file() && !path.to_string_lossy().ends_with(".metadata") {
667                        let logical_path = path
668                            .to_string_lossy()
669                            .trim_start_matches(root_dir)
670                            .replace('\\', "/")
671                            .to_string();
672                        if logical_path.starts_with(prefix) {
673                            results.push(logical_path);
674                        }
675                    } else if path.is_dir() {
676                        let sub = walk_dir(&path, prefix, root_dir)?;
677                        results.extend(sub);
678                    }
679                }
680            }
681            Ok(results)
682        }
683
684        walk_dir(
685            std::path::Path::new(&prefix_dir),
686            prefix,
687            &self.root_dir,
688        )
689    }
690
691    fn get_metadata(&self, path: &str) -> ExtensionResult<HashMap<String, String>> {
692        if let Some(metadata) = self.metadata_cache.get(path) {
693            return Ok(metadata.clone());
694        }
695        let mut this = self.clone();
696        this.load_metadata(path)
697    }
698
699    fn set_metadata(&mut self, path: &str, key: &str, value: &str) -> ExtensionResult<()> {
700        let mut metadata = self.get_metadata(path)?;
701        metadata.insert(key.to_string(), value.to_string());
702        self.metadata_cache
703            .insert(path.to_string(), metadata.clone());
704        self.save_metadata(path, &metadata)
705    }
706
707    fn flush(&mut self) -> ExtensionResult<()> {
708        for (path, metadata) in &self.metadata_cache {
709            self.save_metadata(path, metadata)?;
710        }
711        Ok(())
712    }
713
714    fn begin_transaction(&mut self) -> ExtensionResult<()> {
715        Err(ExtensionError::Unsupported(
716            "Transactions not supported".to_string(),
717        ))
718    }
719
720    fn commit_transaction(&mut self) -> ExtensionResult<()> {
721        Err(ExtensionError::Unsupported(
722            "Transactions not supported".to_string(),
723        ))
724    }
725
726    fn rollback_transaction(&mut self) -> ExtensionResult<()> {
727        Err(ExtensionError::Unsupported(
728            "Transactions not supported".to_string(),
729        ))
730    }
731
732    fn supports_transactions(&self) -> bool {
733        false
734    }
735
736    fn is_available(&self) -> bool {
737        std::path::Path::new(&self.root_dir).exists()
738    }
739
740    fn stats(&self) -> ExtensionResult<HashMap<String, String>> {
741        let mut stats = HashMap::new();
742
743        fn walk_dir_stats(dir: &std::path::Path) -> ExtensionResult<(usize, u64)> {
744            let mut count = 0;
745            let mut size = 0;
746            if dir.exists() && dir.is_dir() {
747                for entry in std::fs::read_dir(dir)
748                    .map_err(|e| ExtensionError::Storage(format!("Failed to read directory: {}", e)))?
749                {
750                    let entry = entry.map_err(|e| {
751                        ExtensionError::Storage(format!("Failed to read entry: {}", e))
752                    })?;
753                    let path = entry.path();
754                    if path.is_file() && !path.to_string_lossy().ends_with(".metadata") {
755                        count += 1;
756                        size += entry
757                            .metadata()
758                            .map_err(|e| {
759                                ExtensionError::Storage(format!("Failed to get metadata: {}", e))
760                            })?
761                            .len();
762                    } else if path.is_dir() {
763                        let (sc, ss) = walk_dir_stats(&path)?;
764                        count += sc;
765                        size += ss;
766                    }
767                }
768            }
769            Ok((count, size))
770        }
771
772        let (file_count, total_size) =
773            walk_dir_stats(std::path::Path::new(&self.root_dir))?;
774
775        stats.insert("file_count".to_string(), file_count.to_string());
776        stats.insert("total_size_bytes".to_string(), total_size.to_string());
777        stats.insert("root_dir".to_string(), self.root_dir.clone());
778
779        Ok(stats)
780    }
781}
782
783impl Clone for FileSystemStorageProvider {
784    fn clone(&self) -> Self {
785        Self {
786            root_dir: self.root_dir.clone(),
787            name: self.name.clone(),
788            version: self.version.clone(),
789            description: self.description.clone(),
790            ext_metadata: self.ext_metadata.clone(),
791            metadata_cache: self.metadata_cache.clone(),
792        }
793    }
794}
795
796#[cfg(test)]
797mod tests {
798    use super::*;
799    use tempfile::tempdir;
800
801    #[test]
802    fn test_extension_manager() {
803        let mut manager = HTTExtensionManager::new();
804
805        struct TestExtension;
806
807        impl HTTExtensionBase for TestExtension {
808            fn name(&self) -> &str {
809                "test"
810            }
811            fn version(&self) -> &str {
812                "1.0.0"
813            }
814            fn description(&self) -> &str {
815                "Test extension"
816            }
817            fn capabilities(&self) -> ExtensionCapabilities {
818                ExtensionCapabilities::none()
819            }
820            fn metadata(&self) -> HashMap<String, String> {
821                HashMap::new()
822            }
823            fn initialize(&mut self) -> ExtensionResult<()> {
824                Ok(())
825            }
826            fn shutdown(&mut self) -> ExtensionResult<()> {
827                Ok(())
828            }
829            fn is_compatible_with(&self, _data_type: &str) -> bool {
830                true
831            }
832            fn as_any(&self) -> &dyn Any {
833                self
834            }
835            fn as_any_mut(&mut self) -> &mut dyn Any {
836                self
837            }
838        }
839
840        let result = manager.register_extension(TestExtension, HashMap::new());
841        assert!(result.is_ok());
842
843        assert!(manager.has_extension("test"));
844
845        let extension = manager.get_extension("test");
846        assert!(extension.is_some());
847        assert_eq!(extension.unwrap().name(), "test");
848
849        let result = manager.remove_extension("test");
850        assert!(result.is_ok());
851        assert!(!manager.has_extension("test"));
852    }
853
854    #[test]
855    fn test_filesystem_provider() {
856        let temp_dir = tempdir().unwrap();
857        let root_dir = temp_dir.path().to_string_lossy().to_string();
858
859        let mut provider = FileSystemStorageProvider::new(root_dir.clone());
860
861        let result = provider.initialize();
862        assert!(result.is_ok());
863
864        let data = b"test data";
865        let result = provider.store("/test.txt", data);
866        assert!(result.is_ok());
867
868        let exists = provider.exists("/test.txt").unwrap();
869        assert!(exists);
870
871        let retrieved = provider.retrieve("/test.txt").unwrap();
872        assert_eq!(retrieved, data);
873
874        let result = provider.set_metadata("/test.txt", "content_type", "text/plain");
875        assert!(result.is_ok());
876
877        let metadata = provider.get_metadata("/test.txt").unwrap();
878        assert_eq!(
879            metadata.get("content_type"),
880            Some(&"text/plain".to_string())
881        );
882
883        let files = provider.list("/").unwrap();
884        assert_eq!(files.len(), 1);
885        assert_eq!(files[0], "/test.txt");
886
887        let result = provider.delete("/test.txt");
888        assert!(result.is_ok());
889
890        let exists = provider.exists("/test.txt").unwrap();
891        assert!(!exists);
892    }
893}