1use 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
21pub type ExtensionResult<T> = Result<T, ExtensionError>;
23
24#[derive(Debug)]
26pub enum ExtensionError {
27 Storage(String),
29 Processing(String),
31 Configuration(String),
33 Serialization(String),
35 Backend(String),
37 Unsupported(String),
39 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub struct ExtensionCapabilities {
74 pub can_store: bool,
76 pub can_retrieve: bool,
78 pub can_process: bool,
80 pub can_enhance_queries: bool,
82 pub can_optimize: bool,
84 pub can_analyze_semantics: bool,
86}
87
88impl ExtensionCapabilities {
89 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 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 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 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 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
150pub trait HTTExtensionBase: Send + Sync {
152 fn name(&self) -> &str;
154
155 fn version(&self) -> &str;
157
158 fn description(&self) -> &str;
160
161 fn capabilities(&self) -> ExtensionCapabilities;
163
164 fn metadata(&self) -> HashMap<String, String>;
166
167 fn initialize(&mut self) -> ExtensionResult<()>;
169
170 fn shutdown(&mut self) -> ExtensionResult<()>;
172
173 fn is_compatible_with(&self, data_type: &str) -> bool;
175
176 fn as_any(&self) -> &dyn Any;
178
179 fn as_any_mut(&mut self) -> &mut dyn Any;
181}
182
183pub trait HTTExtension: HTTExtensionBase {
188 fn process_node(&self, node: &CompressedNode) -> ExtensionResult<CompressedNode>;
190
191 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 fn enhance_query(
202 &self,
203 path: &str,
204 query_params: &HashMap<String, String>,
205 ) -> ExtensionResult<(String, HashMap<String, String>)>;
206
207 fn extract_info(&self, node: &CompressedNode) -> ExtensionResult<HashMap<String, String>>;
209
210 fn can_process(&self, node: &CompressedNode) -> bool;
212}
213
214pub trait HTTStorageProvider: HTTExtensionBase {
218 fn store(&mut self, path: &str, data: &[u8]) -> ExtensionResult<()>;
220
221 fn retrieve(&self, path: &str) -> ExtensionResult<Vec<u8>>;
223
224 fn delete(&mut self, path: &str) -> ExtensionResult<()>;
226
227 fn exists(&self, path: &str) -> ExtensionResult<bool>;
229
230 fn list(&self, prefix: &str) -> ExtensionResult<Vec<String>>;
232
233 fn get_metadata(&self, path: &str) -> ExtensionResult<HashMap<String, String>>;
235
236 fn set_metadata(&mut self, path: &str, key: &str, value: &str) -> ExtensionResult<()>;
238
239 fn flush(&mut self) -> ExtensionResult<()>;
241
242 fn begin_transaction(&mut self) -> ExtensionResult<()>;
244
245 fn commit_transaction(&mut self) -> ExtensionResult<()>;
247
248 fn rollback_transaction(&mut self) -> ExtensionResult<()>;
250
251 fn supports_transactions(&self) -> bool;
253
254 fn is_available(&self) -> bool;
256
257 fn stats(&self) -> ExtensionResult<HashMap<String, String>>;
259}
260
261pub struct HTTExtensionManager {
263 extensions: HashMap<String, Box<dyn HTTExtensionBase>>,
265 configs: HashMap<String, HashMap<String, String>>,
267 dependency_order: Vec<String>,
269 capabilities_cache: HashMap<String, ExtensionCapabilities>,
271}
272
273impl HTTExtensionManager {
274 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 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 pub fn get_extension(&self, name: &str) -> Option<&dyn HTTExtensionBase> {
306 self.extensions.get(name).map(|ext| ext.as_ref())
307 }
308
309 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 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 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 pub fn extension_names(&self) -> Vec<String> {
331 self.extensions.keys().cloned().collect()
332 }
333
334 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 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 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 pub fn get_config(&self, name: &str) -> Option<&HashMap<String, String>> {
368 self.configs.get(name)
369 }
370
371 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 pub fn has_extension(&self, name: &str) -> bool {
390 self.extensions.contains_key(name)
391 }
392
393 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
424pub struct ExtensionRegistry {
426 manager: Arc<RwLock<HTTExtensionManager>>,
428}
429
430impl ExtensionRegistry {
431 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 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 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
464pub struct FileSystemStorageProvider {
466 root_dir: String,
468 name: String,
470 version: String,
472 description: String,
474 ext_metadata: HashMap<String, String>,
476 metadata_cache: HashMap<String, HashMap<String, String>>,
478}
479
480impl FileSystemStorageProvider {
481 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 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}