Files
Oxicloud/src/application/ports/storage_ports.rs
T

177 lines
7.1 KiB
Rust
Raw Normal View History

2025-03-19 19:52:12 +01:00
use std::path::PathBuf;
use async_trait::async_trait;
use bytes::Bytes;
use futures::Stream;
2025-04-13 01:04:04 +02:00
use serde_json::Value;
2025-03-19 19:52:12 +01:00
use crate::domain::entities::file::File;
use crate::domain::services::path_service::StoragePath;
use crate::common::errors::DomainError;
// Re-export domain repository traits for backward compatibility.
// The canonical definitions now live in domain/repositories/.
pub use crate::domain::repositories::file_repository::{FileReadRepository, FileWriteRepository, FileRepository};
pub use crate::domain::repositories::folder_repository::FolderRepository;
// ─────────────────────────────────────────────────────
// FileReadPort — application-layer alias for FileReadRepository
// ─────────────────────────────────────────────────────
/// Secondary port for file **reading**.
///
/// Encapsulates every operation that queries state without modifying it:
/// get, list, content, stream, mmap, range, path resolution.
2025-03-19 19:52:12 +01:00
#[async_trait]
pub trait FileReadPort: Send + Sync + 'static {
/// Gets a file by its ID.
2025-03-19 19:52:12 +01:00
async fn get_file(&self, id: &str) -> Result<File, DomainError>;
/// Lists files in a folder.
2025-03-19 19:52:12 +01:00
async fn list_files(&self, folder_id: Option<&str>) -> Result<Vec<File>, DomainError>;
/// Gets the full content as bytes (small/medium files only).
2025-03-19 19:52:12 +01:00
async fn get_file_content(&self, id: &str) -> Result<Vec<u8>, DomainError>;
/// Gets content as a stream (ideal for large files).
async fn get_file_stream(
&self,
id: &str,
) -> Result<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>, DomainError>;
/// Stream of a byte range (HTTP Range Requests, video seek).
async fn get_file_range_stream(
&self,
id: &str,
start: u64,
end: Option<u64>,
) -> Result<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>, DomainError>;
/// Memory-map of a file for zero-copy access (10–100 MB).
async fn get_file_mmap(&self, id: &str) -> Result<Bytes, DomainError>;
/// Gets the logical storage path of a file.
async fn get_file_path(&self, id: &str) -> Result<StoragePath, DomainError>;
/// Gets the parent folder ID from a path (WebDAV).
async fn get_parent_folder_id(&self, path: &str) -> Result<String, DomainError>;
2025-03-19 19:52:12 +01:00
}
// ─────────────────────────────────────────────────────
// FileWritePort — all write / mutate operations
// ─────────────────────────────────────────────────────
/// Secondary port for file **writing**.
///
/// Covers: upload (buffered + streaming), move, delete, update,
/// and deferred registration for the write-behind cache.
2025-03-19 19:52:12 +01:00
#[async_trait]
pub trait FileWritePort: Send + Sync + 'static {
/// Saves a new file from bytes.
2025-03-19 19:52:12 +01:00
async fn save_file(
&self,
name: String,
folder_id: Option<String>,
content_type: String,
content: Vec<u8>,
) -> Result<File, DomainError>;
/// Streaming upload — writes chunks to disk without accumulating in RAM.
async fn save_file_from_stream(
&self,
name: String,
folder_id: Option<String>,
content_type: String,
stream: std::pin::Pin<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>>,
) -> Result<File, DomainError>;
/// Moves a file to another folder.
async fn move_file(
&self,
file_id: &str,
target_folder_id: Option<String>,
) -> Result<File, DomainError>;
/// Renames a file (same folder, different name).
2026-02-08 22:44:42 +01:00
async fn rename_file(
&self,
file_id: &str,
new_name: &str,
) -> Result<File, DomainError>;
/// Deletes a file.
2025-03-19 19:52:12 +01:00
async fn delete_file(&self, id: &str) -> Result<(), DomainError>;
/// Updates the content of an existing file.
async fn update_file_content(&self, file_id: &str, content: Vec<u8>) -> Result<(), DomainError>;
/// Registers file metadata WITHOUT writing content to disk (write-behind).
///
/// Returns `(File, PathBuf)` where `PathBuf` is the destination path for the
/// deferred write that the `WriteBehindCache` will perform.
async fn register_file_deferred(
&self,
name: String,
folder_id: Option<String>,
content_type: String,
size: u64,
) -> Result<(File, PathBuf), DomainError>;
// ── Trash operations ──
/// Moves a file to the trash
async fn move_to_trash(&self, file_id: &str) -> Result<(), DomainError>;
/// Restores a file from the trash to its original location
async fn restore_from_trash(&self, file_id: &str, original_path: &str) -> Result<(), DomainError>;
/// Permanently deletes a file (used by the trash)
async fn delete_file_permanently(&self, file_id: &str) -> Result<(), DomainError>;
2025-03-19 19:52:12 +01:00
}
// ─────────────────────────────────────────────────────
// Auxiliary ports (unchanged)
// ─────────────────────────────────────────────────────
/// Secondary port for file path resolution
2025-03-19 19:52:12 +01:00
#[async_trait]
pub trait FilePathResolutionPort: Send + Sync + 'static {
/// Gets the storage path of a file
2025-03-19 19:52:12 +01:00
async fn get_file_path(&self, id: &str) -> Result<StoragePath, DomainError>;
/// Resolves a domain path to a physical path
2025-03-19 19:52:12 +01:00
fn resolve_path(&self, storage_path: &StoragePath) -> PathBuf;
}
/// Secondary port for file/directory existence verification
2025-03-19 19:52:12 +01:00
#[async_trait]
pub trait StorageVerificationPort: Send + Sync + 'static {
/// Checks whether a file exists at the given path
2025-03-19 19:52:12 +01:00
async fn file_exists(&self, storage_path: &StoragePath) -> Result<bool, DomainError>;
/// Checks whether a directory exists at the given path
2025-03-19 19:52:12 +01:00
async fn directory_exists(&self, storage_path: &StoragePath) -> Result<bool, DomainError>;
}
/// Secondary port for directory management
2025-03-19 19:52:12 +01:00
#[async_trait]
pub trait DirectoryManagementPort: Send + Sync + 'static {
/// Creates directories if they do not exist
2025-03-19 19:52:12 +01:00
async fn ensure_directory(&self, storage_path: &StoragePath) -> Result<(), DomainError>;
2025-04-09 00:21:20 +02:00
}
/// Secondary port for storage usage management
2025-04-09 00:21:20 +02:00
#[async_trait]
pub trait StorageUsagePort: Send + Sync + 'static {
/// Updates storage usage statistics for a user
2025-04-09 00:21:20 +02:00
async fn update_user_storage_usage(&self, user_id: &str) -> Result<i64, DomainError>;
/// Updates storage usage statistics for all users
2025-04-09 00:21:20 +02:00
async fn update_all_users_storage_usage(&self) -> Result<(), DomainError>;
2025-04-13 01:04:04 +02:00
}
/// Generic storage service interface for calendar and contact services
#[async_trait]
pub trait StorageUseCase: Send + Sync + 'static {
/// Handle a request with the specified action and parameters
async fn handle_request(&self, action: &str, params: Value) -> Result<Value, DomainError>;
2025-03-19 19:52:12 +01:00
}