1use crate::{
8 embedding::SpeakerEmbedding, quality::QualityMetrics, Error, Result, SpeakerProfile,
9 VoiceCloneResult, VoiceSample,
10};
11use serde::{Deserialize, Serialize};
12use std::collections::{BTreeMap, HashMap, VecDeque};
13use std::fs::{self, File};
14use std::io::{BufReader, BufWriter, Read, Write};
15use std::path::{Path, PathBuf};
16use std::sync::Arc;
17use std::time::{Duration, Instant, SystemTime};
18use tokio::sync::{Mutex, RwLock};
19use tracing::{debug, error, info, warn};
20use uuid::Uuid;
21
22#[derive(Debug)]
24pub struct VoiceModelStorage {
25 config: StorageConfig,
27 storage_root: PathBuf,
29 metadata_index: Arc<RwLock<MetadataIndex>>,
31 model_cache: Arc<RwLock<ModelCache>>,
33 statistics: Arc<RwLock<StorageStatistics>>,
35 maintenance_tasks: Arc<Mutex<Vec<tokio::task::JoinHandle<()>>>>,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct StorageConfig {
42 pub max_cache_size: usize,
44 pub enable_compression: bool,
46 pub compression_level: u32,
48 pub max_model_size: u64,
50 pub enable_auto_cleanup: bool,
52 pub cleanup_age_threshold_days: u64,
54 pub enable_encryption: bool,
56 pub maintenance_interval: Duration,
58 pub enable_deduplication: bool,
60 pub deduplication_threshold: f32,
62 pub enable_tiered_storage: bool,
64 pub backup_retention_days: u64,
66}
67
68#[derive(Debug, Clone, Default)]
70struct MetadataIndex {
71 speaker_metadata: HashMap<String, StoredModelMetadata>,
73 category_index: HashMap<String, Vec<String>>,
75 creation_time_index: BTreeMap<SystemTime, Vec<String>>,
77 access_frequency: HashMap<String, AccessStats>,
79 size_index: BTreeMap<u64, Vec<String>>,
81}
82
83#[derive(Debug)]
85struct ModelCache {
86 cache: HashMap<String, CachedModel>,
88 access_queue: VecDeque<String>,
90 current_size: u64,
92 max_size: u64,
94 stats: CacheStatistics,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct StoredModelMetadata {
101 pub model_id: String,
103 pub speaker_info: SpeakerInfo,
105 pub storage_info: StorageInfo,
107 pub quality_metrics: Option<QualityMetrics>,
109 pub access_stats: AccessStats,
111 pub compression_info: Option<CompressionInfo>,
113 pub tags: Vec<String>,
115 pub custom_metadata: HashMap<String, String>,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct SpeakerInfo {
122 pub speaker_id: String,
124 pub name: Option<String>,
126 pub characteristics: VoiceCharacteristicsSummary,
128 pub languages: Vec<String>,
130 pub gender: Option<String>,
132 pub age_group: Option<String>,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct StorageInfo {
139 pub file_path: PathBuf,
141 pub file_size: u64,
143 pub created_at: SystemTime,
145 pub modified_at: SystemTime,
147 pub last_accessed: SystemTime,
149 pub storage_tier: StorageTier,
151 pub checksum: String,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct VoiceCharacteristicsSummary {
158 pub average_f0: f32,
160 pub quality_indicators: Vec<f32>,
162 pub spectral_centroid: f32,
164 pub energy_stats: EnergyStats,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct EnergyStats {
171 pub mean: f32,
172 pub std_dev: f32,
173 pub dynamic_range: f32,
174}
175
176#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct AccessStats {
179 pub access_count: u64,
181 pub last_access: SystemTime,
183 pub access_frequency: f32,
185 pub recent_accesses: VecDeque<SystemTime>,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct CompressionInfo {
192 pub algorithm: CompressionAlgorithm,
194 pub original_size: u64,
196 pub compressed_size: u64,
198 pub compression_ratio: f32,
200 pub compression_time: Duration,
202}
203
204#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
206pub enum StorageTier {
207 Hot,
209 Warm,
211 Cold,
213}
214
215#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
217pub enum CompressionAlgorithm {
218 None,
219 Gzip,
220 Zstd,
221 Lz4,
222}
223
224#[derive(Debug)]
226struct CachedModel {
227 data: Vec<u8>,
229 metadata: StoredModelMetadata,
231 cached_at: SystemTime,
233 access_count: u64,
235 size: u64,
237}
238
239#[derive(Debug, Clone, Default, Serialize, Deserialize)]
241pub struct CacheStatistics {
242 pub hits: u64,
244 pub misses: u64,
246 pub hit_ratio: f32,
248 pub evictions: u64,
250 pub avg_load_time_ms: f32,
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct StorageStatistics {
257 pub total_models: u64,
259 pub total_size: u64,
261 pub avg_model_size: u64,
263 pub tier_distribution: HashMap<StorageTier, u64>,
265 pub compression_stats: CompressionStatistics,
267 pub cache_stats: CacheStatistics,
269 pub maintenance_stats: MaintenanceStatistics,
271 pub health_indicators: HealthIndicators,
273}
274
275#[derive(Debug, Clone, Default, Serialize, Deserialize)]
277pub struct CompressionStatistics {
278 pub compressed_models: u64,
280 pub total_original_size: u64,
282 pub total_compressed_size: u64,
284 pub avg_compression_ratio: f32,
286 pub space_saved: u64,
288}
289
290#[derive(Debug, Clone, Default, Serialize, Deserialize)]
292pub struct MaintenanceStatistics {
293 pub last_maintenance: Option<SystemTime>,
295 pub cleanup_operations: u64,
297 pub models_cleaned: u64,
299 pub space_recovered: u64,
301 pub deduplication_count: u64,
303 pub models_deduplicated: u64,
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize)]
309pub struct HealthIndicators {
310 pub health_score: f32,
312 pub storage_utilization: f32,
314 pub cache_efficiency: f32,
316 pub error_rate: f32,
318 pub avg_response_time_ms: f32,
320 pub issues: Vec<String>,
322 pub recommendations: Vec<String>,
324}
325
326#[derive(Debug, Clone, Serialize, Deserialize)]
328pub struct StorageOperationResult {
329 pub success: bool,
331 pub model_id: String,
333 pub operation: StorageOperation,
335 pub processing_time: Duration,
337 pub bytes_affected: u64,
339 pub error_message: Option<String>,
341 pub metadata: HashMap<String, String>,
343}
344
345#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
347pub enum StorageOperation {
348 Store,
349 Retrieve,
350 Delete,
351 Update,
352 Compress,
353 Migrate,
354 Backup,
355 Restore,
356}
357
358impl VoiceModelStorage {
359 pub async fn new(storage_root: PathBuf, config: StorageConfig) -> Result<Self> {
361 fs::create_dir_all(&storage_root)
363 .map_err(|e| Error::Config(format!("Failed to create storage directory: {e}")))?;
364
365 info!("Initializing voice model storage at: {:?}", storage_root);
366
367 let storage = Self {
368 config: config.clone(),
369 storage_root,
370 metadata_index: Arc::new(RwLock::new(MetadataIndex::default())),
371 model_cache: Arc::new(RwLock::new(ModelCache::new(
372 (config.max_cache_size * 1024 * 1024) as u64,
373 ))),
374 statistics: Arc::new(RwLock::new(StorageStatistics::default())),
375 maintenance_tasks: Arc::new(Mutex::new(Vec::new())),
376 };
377
378 storage.load_metadata_index().await?;
380
381 if config.enable_auto_cleanup || config.enable_deduplication {
383 storage.start_maintenance_tasks().await?;
384 }
385
386 info!("Voice model storage initialized successfully");
387 Ok(storage)
388 }
389
390 pub async fn store_model(
392 &self,
393 speaker_profile: &SpeakerProfile,
394 model_data: &[u8],
395 quality_metrics: Option<QualityMetrics>,
396 tags: Vec<String>,
397 ) -> Result<StorageOperationResult> {
398 let start_time = Instant::now();
399 let model_id = Uuid::new_v4().to_string();
400
401 debug!("Storing voice model: {}", model_id);
402
403 if self.config.enable_deduplication {
405 if let Some(existing_id) = self.find_similar_model(speaker_profile).await? {
406 info!("Found similar model, using existing: {}", existing_id);
407 return Ok(StorageOperationResult {
408 success: true,
409 model_id: existing_id,
410 operation: StorageOperation::Store,
411 processing_time: start_time.elapsed(),
412 bytes_affected: 0,
413 error_message: None,
414 metadata: [("deduplicated".to_string(), "true".to_string())].into(),
415 });
416 }
417 }
418
419 let file_path = self.generate_storage_path(&model_id)?;
421 let full_path = self.storage_root.join(&file_path);
422
423 if let Some(parent) = full_path.parent() {
425 fs::create_dir_all(parent)
426 .map_err(|e| Error::Processing(format!("Failed to create model directory: {e}")))?;
427 }
428
429 let (final_data, compression_info) = if self.config.enable_compression {
431 self.compress_model_data(model_data).await?
432 } else {
433 (model_data.to_vec(), None)
434 };
435
436 let mut file = File::create(&full_path)
438 .map_err(|e| Error::Processing(format!("Failed to create model file: {e}")))?;
439 file.write_all(&final_data)
440 .map_err(|e| Error::Processing(format!("Failed to write model data: {e}")))?;
441
442 let checksum = self.calculate_checksum(&final_data);
444
445 let metadata = StoredModelMetadata {
447 model_id: model_id.clone(),
448 speaker_info: self.extract_speaker_info(speaker_profile),
449 storage_info: StorageInfo {
450 file_path: file_path.clone(),
451 file_size: final_data.len() as u64,
452 created_at: SystemTime::now(),
453 modified_at: SystemTime::now(),
454 last_accessed: SystemTime::now(),
455 storage_tier: StorageTier::Hot,
456 checksum,
457 },
458 quality_metrics,
459 access_stats: AccessStats {
460 access_count: 0,
461 last_access: SystemTime::now(),
462 access_frequency: 0.0,
463 recent_accesses: VecDeque::new(),
464 },
465 compression_info,
466 tags,
467 custom_metadata: HashMap::new(),
468 };
469
470 self.update_metadata_index(&metadata).await?;
472
473 if self.should_cache_model(&metadata).await {
475 self.cache_model(&model_id, &final_data, &metadata).await?;
476 }
477
478 self.update_storage_statistics(&metadata, StorageOperation::Store)
480 .await;
481
482 let processing_time = start_time.elapsed();
483 info!(
484 "Stored voice model {} in {:?} (size: {} bytes)",
485 model_id,
486 processing_time,
487 final_data.len()
488 );
489
490 Ok(StorageOperationResult {
491 success: true,
492 model_id,
493 operation: StorageOperation::Store,
494 processing_time,
495 bytes_affected: final_data.len() as u64,
496 error_message: None,
497 metadata: HashMap::new(),
498 })
499 }
500
501 pub async fn retrieve_model(&self, model_id: &str) -> Result<(Vec<u8>, StoredModelMetadata)> {
503 let start_time = Instant::now();
504
505 debug!("Retrieving voice model: {}", model_id);
506
507 if let Some((data, metadata)) = self.get_from_cache(model_id).await? {
509 self.update_access_stats(model_id).await?;
510 debug!("Retrieved model from cache: {}", model_id);
511 return Ok((data, metadata));
512 }
513
514 let metadata = self
516 .get_model_metadata(model_id)
517 .await?
518 .ok_or_else(|| Error::Processing(format!("Model not found: {model_id}")))?;
519
520 let file_path = self.storage_root.join(&metadata.storage_info.file_path);
521 let mut file = File::open(&file_path)
522 .map_err(|e| Error::Processing(format!("Failed to open model file: {e}")))?;
523
524 let mut data = Vec::new();
525 file.read_to_end(&mut data)
526 .map_err(|e| Error::Processing(format!("Failed to read model data: {e}")))?;
527
528 let checksum = self.calculate_checksum(&data);
530 if checksum != metadata.storage_info.checksum {
531 return Err(Error::Processing(format!(
532 "Model data corrupted: {}",
533 model_id
534 )));
535 }
536
537 let final_data = if let Some(compression_info) = &metadata.compression_info {
539 self.decompress_model_data(&data, compression_info.algorithm)
540 .await?
541 } else {
542 data
543 };
544
545 if self.should_cache_model(&metadata).await {
547 self.cache_model(model_id, &final_data, &metadata).await?;
548 }
549
550 self.update_access_stats(model_id).await?;
552
553 let processing_time = start_time.elapsed();
554 debug!(
555 "Retrieved voice model {} in {:?} (size: {} bytes)",
556 model_id,
557 processing_time,
558 final_data.len()
559 );
560
561 Ok((final_data, metadata))
562 }
563
564 pub async fn delete_model(&self, model_id: &str) -> Result<StorageOperationResult> {
566 let start_time = Instant::now();
567
568 info!("Deleting voice model: {}", model_id);
569
570 let metadata = self
571 .get_model_metadata(model_id)
572 .await?
573 .ok_or_else(|| Error::Processing(format!("Model not found: {model_id}")))?;
574
575 self.remove_from_cache(model_id).await;
577
578 let file_path = self.storage_root.join(&metadata.storage_info.file_path);
580 if file_path.exists() {
581 fs::remove_file(&file_path)
582 .map_err(|e| Error::Processing(format!("Failed to delete model file: {e}")))?;
583 }
584
585 self.remove_from_metadata_index(model_id).await?;
587
588 self.update_storage_statistics(&metadata, StorageOperation::Delete)
590 .await;
591
592 let processing_time = start_time.elapsed();
593 info!("Deleted voice model {} in {:?}", model_id, processing_time);
594
595 Ok(StorageOperationResult {
596 success: true,
597 model_id: model_id.to_string(),
598 operation: StorageOperation::Delete,
599 processing_time,
600 bytes_affected: metadata.storage_info.file_size,
601 error_message: None,
602 metadata: HashMap::new(),
603 })
604 }
605
606 pub async fn list_models(
608 &self,
609 filter: Option<ModelFilter>,
610 limit: Option<usize>,
611 offset: Option<usize>,
612 ) -> Result<Vec<StoredModelMetadata>> {
613 let index = self.metadata_index.read().await;
614 let mut models: Vec<_> = index.speaker_metadata.values().cloned().collect();
615
616 if let Some(filter) = filter {
618 models = self.apply_filter(models, &filter);
619 }
620
621 models.sort_by(|a, b| b.storage_info.created_at.cmp(&a.storage_info.created_at));
623
624 let start = offset.unwrap_or(0);
626 let end = if let Some(limit) = limit {
627 (start + limit).min(models.len())
628 } else {
629 models.len()
630 };
631
632 Ok(models[start..end].to_vec())
633 }
634
635 pub async fn get_statistics(&self) -> StorageStatistics {
637 self.statistics.read().await.clone()
638 }
639
640 pub async fn perform_maintenance(&self) -> Result<MaintenanceReport> {
642 info!("Starting storage maintenance");
643 let start_time = Instant::now();
644
645 let mut report = MaintenanceReport {
646 start_time: SystemTime::now(),
647 operations_performed: Vec::new(),
648 models_processed: 0,
649 space_recovered: 0,
650 errors: Vec::new(),
651 duration: Duration::from_secs(0), };
653
654 if self.config.enable_auto_cleanup {
656 match self.cleanup_old_models().await {
657 Ok((count, space)) => {
658 report.operations_performed.push("cleanup".to_string());
659 report.models_processed += count;
660 report.space_recovered += space;
661 }
662 Err(e) => report.errors.push(format!("Cleanup failed: {e}")),
663 }
664 }
665
666 if self.config.enable_deduplication {
668 match self.deduplicate_models().await {
669 Ok((count, space)) => {
670 report
671 .operations_performed
672 .push("deduplication".to_string());
673 report.models_processed += count;
674 report.space_recovered += space;
675 }
676 Err(e) => report.errors.push(format!("Deduplication failed: {e}")),
677 }
678 }
679
680 match self.update_storage_tiers().await {
682 Ok(count) => {
683 report.operations_performed.push("tier_update".to_string());
684 report.models_processed += count;
685 }
686 Err(e) => report.errors.push(format!("Tier update failed: {e}")),
687 }
688
689 self.optimize_metadata_index().await?;
691 report
692 .operations_performed
693 .push("index_optimization".to_string());
694
695 report.duration = start_time.elapsed();
696 info!("Storage maintenance completed in {:?}", report.duration);
697
698 Ok(report)
699 }
700
701 fn generate_storage_path(&self, model_id: &str) -> Result<PathBuf> {
705 let prefix = &model_id[0..2];
707 let subdir = &model_id[2..4];
708 Ok(PathBuf::from(format!(
709 "models/{}/{}/{}.voice",
710 prefix, subdir, model_id
711 )))
712 }
713
714 fn extract_speaker_info(&self, profile: &SpeakerProfile) -> SpeakerInfo {
716 let characteristics = VoiceCharacteristicsSummary {
717 average_f0: profile.characteristics.average_pitch,
718 quality_indicators: vec![
719 profile.characteristics.voice_quality.breathiness,
720 profile.characteristics.voice_quality.roughness,
721 profile.characteristics.voice_quality.brightness,
722 profile.characteristics.voice_quality.warmth,
723 ],
724 spectral_centroid: 2000.0, energy_stats: EnergyStats {
726 mean: profile.characteristics.average_energy,
727 std_dev: 0.1, dynamic_range: 40.0, },
730 };
731
732 SpeakerInfo {
733 speaker_id: profile.id.clone(),
734 name: Some(profile.name.clone()),
735 characteristics,
736 languages: profile.languages.clone(),
737 gender: profile.characteristics.gender.map(|g| format!("{:?}", g)),
738 age_group: profile
739 .characteristics
740 .age_group
741 .map(|a| format!("{:?}", a)),
742 }
743 }
744
745 fn calculate_checksum(&self, data: &[u8]) -> String {
747 use std::collections::hash_map::DefaultHasher;
748 use std::hash::{Hash, Hasher};
749
750 let mut hasher = DefaultHasher::new();
751 data.hash(&mut hasher);
752 format!("{:x}", hasher.finish())
753 }
754
755 async fn load_metadata_index(&self) -> Result<()> {
757 Ok(())
760 }
761
762 async fn update_metadata_index(&self, metadata: &StoredModelMetadata) -> Result<()> {
764 let mut index = self.metadata_index.write().await;
765
766 index
767 .speaker_metadata
768 .insert(metadata.model_id.clone(), metadata.clone());
769
770 for tag in &metadata.tags {
772 index
773 .category_index
774 .entry(tag.clone())
775 .or_insert_with(Vec::new)
776 .push(metadata.model_id.clone());
777 }
778
779 index
781 .creation_time_index
782 .entry(metadata.storage_info.created_at)
783 .or_insert_with(Vec::new)
784 .push(metadata.model_id.clone());
785
786 index
788 .size_index
789 .entry(metadata.storage_info.file_size)
790 .or_insert_with(Vec::new)
791 .push(metadata.model_id.clone());
792
793 Ok(())
794 }
795
796 async fn compress_model_data(&self, data: &[u8]) -> Result<(Vec<u8>, Option<CompressionInfo>)> {
798 Ok((data.to_vec(), None))
800 }
801
802 async fn decompress_model_data(
803 &self,
804 data: &[u8],
805 _algorithm: CompressionAlgorithm,
806 ) -> Result<Vec<u8>> {
807 Ok(data.to_vec())
809 }
810
811 async fn find_similar_model(&self, _profile: &SpeakerProfile) -> Result<Option<String>> {
812 Ok(None)
814 }
815
816 async fn should_cache_model(&self, _metadata: &StoredModelMetadata) -> bool {
817 true
819 }
820
821 async fn cache_model(
822 &self,
823 _model_id: &str,
824 _data: &[u8],
825 _metadata: &StoredModelMetadata,
826 ) -> Result<()> {
827 Ok(())
829 }
830
831 async fn get_from_cache(
832 &self,
833 _model_id: &str,
834 ) -> Result<Option<(Vec<u8>, StoredModelMetadata)>> {
835 Ok(None)
837 }
838
839 async fn remove_from_cache(&self, _model_id: &str) {
840 }
842
843 async fn get_model_metadata(&self, model_id: &str) -> Result<Option<StoredModelMetadata>> {
844 let index = self.metadata_index.read().await;
845 Ok(index.speaker_metadata.get(model_id).cloned())
846 }
847
848 async fn remove_from_metadata_index(&self, model_id: &str) -> Result<()> {
849 let mut index = self.metadata_index.write().await;
850 index.speaker_metadata.remove(model_id);
851 Ok(())
852 }
853
854 async fn update_access_stats(&self, _model_id: &str) -> Result<()> {
855 Ok(())
857 }
858
859 async fn update_storage_statistics(
860 &self,
861 _metadata: &StoredModelMetadata,
862 _operation: StorageOperation,
863 ) {
864 }
866
867 async fn start_maintenance_tasks(&self) -> Result<()> {
868 Ok(())
870 }
871
872 fn apply_filter(
873 &self,
874 models: Vec<StoredModelMetadata>,
875 _filter: &ModelFilter,
876 ) -> Vec<StoredModelMetadata> {
877 models
879 }
880
881 async fn cleanup_old_models(&self) -> Result<(u64, u64)> {
882 Ok((0, 0))
884 }
885
886 async fn deduplicate_models(&self) -> Result<(u64, u64)> {
887 Ok((0, 0))
889 }
890
891 async fn update_storage_tiers(&self) -> Result<u64> {
892 Ok(0)
894 }
895
896 async fn optimize_metadata_index(&self) -> Result<()> {
897 Ok(())
899 }
900}
901
902#[derive(Debug, Clone, Serialize, Deserialize)]
904pub struct ModelFilter {
905 pub speaker_id: Option<String>,
907 pub tags: Option<Vec<String>>,
909 pub created_after: Option<SystemTime>,
911 pub created_before: Option<SystemTime>,
912 pub storage_tier: Option<StorageTier>,
914 pub min_quality_score: Option<f32>,
916}
917
918#[derive(Debug, Clone, Serialize, Deserialize)]
920pub struct MaintenanceReport {
921 pub start_time: SystemTime,
923 pub operations_performed: Vec<String>,
925 pub models_processed: u64,
927 pub space_recovered: u64,
929 pub errors: Vec<String>,
931 pub duration: Duration,
933}
934
935impl ModelCache {
936 fn new(max_size: u64) -> Self {
937 Self {
938 cache: HashMap::new(),
939 access_queue: VecDeque::new(),
940 current_size: 0,
941 max_size,
942 stats: CacheStatistics::default(),
943 }
944 }
945}
946
947impl Default for StorageConfig {
949 fn default() -> Self {
950 Self {
951 max_cache_size: 100, enable_compression: true,
953 compression_level: 6,
954 max_model_size: 50 * 1024 * 1024, enable_auto_cleanup: true,
956 cleanup_age_threshold_days: 30,
957 enable_encryption: false,
958 maintenance_interval: Duration::from_secs(3600), enable_deduplication: true,
960 deduplication_threshold: 0.95,
961 enable_tiered_storage: true,
962 backup_retention_days: 7,
963 }
964 }
965}
966
967impl Default for StorageStatistics {
968 fn default() -> Self {
969 Self {
970 total_models: 0,
971 total_size: 0,
972 avg_model_size: 0,
973 tier_distribution: HashMap::new(),
974 compression_stats: CompressionStatistics::default(),
975 cache_stats: CacheStatistics::default(),
976 maintenance_stats: MaintenanceStatistics::default(),
977 health_indicators: HealthIndicators {
978 health_score: 1.0,
979 storage_utilization: 0.0,
980 cache_efficiency: 0.0,
981 error_rate: 0.0,
982 avg_response_time_ms: 0.0,
983 issues: Vec::new(),
984 recommendations: Vec::new(),
985 },
986 }
987 }
988}
989
990#[cfg(test)]
991mod tests {
992 use super::*;
993 use tempfile::TempDir;
994
995 #[tokio::test]
996 async fn test_storage_creation() {
997 let temp_dir = TempDir::new().unwrap();
998 let config = StorageConfig::default();
999
1000 let storage = VoiceModelStorage::new(temp_dir.path().to_path_buf(), config).await;
1001 assert!(storage.is_ok());
1002 }
1003
1004 #[tokio::test]
1005 async fn test_storage_config_default() {
1006 let config = StorageConfig::default();
1007 assert_eq!(config.max_cache_size, 100);
1008 assert!(config.enable_compression);
1009 assert_eq!(config.compression_level, 6);
1010 assert!(config.enable_auto_cleanup);
1011 assert!(config.enable_deduplication);
1012 assert_eq!(config.deduplication_threshold, 0.95);
1013 }
1014
1015 #[test]
1016 fn test_storage_tier_enum() {
1017 let tiers = vec![StorageTier::Hot, StorageTier::Warm, StorageTier::Cold];
1018 assert_eq!(tiers.len(), 3);
1019 assert_eq!(format!("{:?}", StorageTier::Hot), "Hot");
1020 }
1021
1022 #[test]
1023 fn test_compression_algorithm_enum() {
1024 let algorithms = vec![
1025 CompressionAlgorithm::None,
1026 CompressionAlgorithm::Gzip,
1027 CompressionAlgorithm::Zstd,
1028 CompressionAlgorithm::Lz4,
1029 ];
1030 assert_eq!(algorithms.len(), 4);
1031 }
1032
1033 #[test]
1034 fn test_storage_operation_enum() {
1035 let operations = vec![
1036 StorageOperation::Store,
1037 StorageOperation::Retrieve,
1038 StorageOperation::Delete,
1039 StorageOperation::Update,
1040 StorageOperation::Compress,
1041 StorageOperation::Migrate,
1042 StorageOperation::Backup,
1043 StorageOperation::Restore,
1044 ];
1045 assert_eq!(operations.len(), 8);
1046 }
1047}