//! `DirectBackend` content-addressed store access: importing files, resolving a //! hash to its path/extension/original name, and removing samples or orphans. use super::{ BackendResult, ConformTarget, DeviceProfileSummary, DirectBackend, ExportBackend, ExportItem, NodeId, Path, PathBuf, StoreBackend, VfsId, }; impl StoreBackend for DirectBackend { // --- Store --- fn import_file(&self, path: &Path) -> BackendResult { let db = self.db.lock(); Ok(self.store.import(path, &db)?) } fn sample_path(&self, hash: &str, ext: &str) -> BackendResult { let db = self.db.lock(); Ok(audiofiles_core::store::resolve_file_path( &self.store, &db, hash, ext, )?) } fn sample_extension(&self, hash: &str) -> BackendResult { let db = self.db.lock(); Ok(audiofiles_core::store::sample_extension(&db, hash)?) } fn sample_original_name(&self, hash: &str) -> BackendResult { let db = self.db.lock(); Ok(audiofiles_core::store::sample_original_name(&db, hash)?) } fn remove_sample(&self, hash: &str) -> BackendResult<()> { let db = self.db.lock(); Ok(self.store.remove(hash, &db)?) } fn remove_orphaned_samples(&self) -> BackendResult { let db = self.db.lock(); Ok(self.store.remove_orphaned_samples(&db)?) } fn cleanup_orphans_local(&self) -> BackendResult { // `remove_orphaned_samples` now suppresses sync triggers internally, so // local orphan GC never pushes a destructive `samples` DELETE across // devices. This wrapper exists only to name the user-facing gesture. let db = self.db.lock(); Ok(self.store.remove_orphaned_samples(&db)?) } fn list_tombstoned(&self) -> BackendResult> { let db = self.db.lock(); Ok(audiofiles_core::store::tombstoned_samples(&db)?) } fn undelete_sample(&self, hash: &str) -> BackendResult<()> { let db = self.db.lock(); audiofiles_core::store::undelete_sample(&db, hash)?; Ok(()) } fn purge_sample(&self, hash: &str) -> BackendResult<()> { let db = self.db.lock(); Ok(self.store.remove(hash, &db)?) } fn sweep_expired_tombstones(&self) -> BackendResult { let db = self.db.lock(); Ok(self.store.sweep_expired_tombstones(&db)?) } fn sample_source_path(&self, hash: &str) -> BackendResult> { let db = self.db.lock(); Ok(audiofiles_core::store::sample_source_path(&db, hash)?) } fn relocate_sample(&self, hash: &str, new_path: &Path) -> BackendResult<()> { let db = self.db.lock(); Ok(audiofiles_core::store::relocate_sample( &self.store, &db, hash, new_path, )?) } fn check_vault_integrity(&self) -> BackendResult<(usize, usize)> { let db = self.db.lock(); Ok(audiofiles_core::store::check_loose_files_integrity(&db)?) } fn purge_missing_loose_files(&self) -> BackendResult { let db = self.db.lock(); Ok(audiofiles_core::store::purge_missing_loose_files(&db)?) } fn relocate_missing_loose_files( &self, search_root: &std::path::Path, ) -> BackendResult<(usize, usize)> { let db = self.db.lock(); Ok(audiofiles_core::store::relocate_missing_loose_files( &db, search_root, )?) } fn start_loose_files_check(&self) -> BackendResult<()> { self.loose_files_send(crate::loose_files_worker::LooseFilesCommand::CheckIntegrity) } fn start_loose_files_relocate(&self, search_root: &std::path::Path) -> BackendResult<()> { self.loose_files_send(crate::loose_files_worker::LooseFilesCommand::Relocate( search_root.to_path_buf(), )) } fn start_loose_files_purge(&self) -> BackendResult<()> { self.loose_files_send(crate::loose_files_worker::LooseFilesCommand::Purge) } fn start_store_verify(&self) -> BackendResult<()> { self.loose_files_send(crate::loose_files_worker::LooseFilesCommand::VerifyStore) } } impl ExportBackend for DirectBackend { // --- Export --- fn collect_export_items( &self, vfs_id: VfsId, parent_id: Option, ) -> BackendResult> { let db = self.db.lock(); Ok(audiofiles_core::export::collect_export_items( &db, vfs_id, parent_id, )?) } fn enrich_export_with_tags(&self, items: &mut [ExportItem]) -> BackendResult<()> { let db = self.db.lock(); audiofiles_core::export::enrich_with_tags(&db, items); Ok(()) } // --- Device profiles --- fn list_device_profiles(&self) -> BackendResult> { #[cfg(feature = "device-profiles")] { Ok(self.plugin_registry.list()) } #[cfg(not(feature = "device-profiles"))] { Ok(Vec::new()) } } fn device_conform_target( &self, profile_name: &str, source_rate: u32, ) -> BackendResult> { #[cfg(feature = "device-profiles")] { Ok(self .plugin_registry .get(profile_name) .map(|plugin| ConformTarget::for_device(&plugin.profile, source_rate))) } #[cfg(not(feature = "device-profiles"))] { let _ = (profile_name, source_rate); Ok(None) } } }