From 476ea9f516a7afaf79832c43f8399c47367027cf Mon Sep 17 00:00:00 2001 From: 409 <409dev@protonmail.com> Date: Wed, 16 Jul 2025 05:19:17 +0200 Subject: [PATCH] backend refactor file_system into warren domain --- backend/src/bin/backend/main.rs | 10 +- backend/src/lib/domain/file_system/mod.rs | 3 - .../src/lib/domain/file_system/models/mod.rs | 1 - backend/src/lib/domain/file_system/ports.rs | 80 ---------- backend/src/lib/domain/mod.rs | 1 - .../file.rs => warren/models/file/mod.rs} | 132 ++-------------- .../lib/domain/warren/models/file/requests.rs | 141 ++++++++++++++++++ backend/src/lib/domain/warren/models/mod.rs | 1 + .../lib/domain/warren/models/warren/mod.rs | 55 +++++++ .../models/{warren.rs => warren/requests.rs} | 51 +------ backend/src/lib/domain/warren/ports.rs | 129 ---------------- .../src/lib/domain/warren/ports/metrics.rs | 47 ++++++ backend/src/lib/domain/warren/ports/mod.rs | 88 +++++++++++ .../src/lib/domain/warren/ports/notifier.rs | 63 ++++++++ .../src/lib/domain/warren/ports/repository.rs | 50 +++++++ .../service/file_system.rs} | 28 +++- backend/src/lib/domain/warren/service/mod.rs | 2 + .../warren/{service.rs => service/warren.rs} | 15 +- .../warrens/create_warren_directory.rs | 10 +- .../warrens/delete_warren_directory.rs | 10 +- .../handlers/warrens/delete_warren_file.rs | 10 +- .../handlers/warrens/list_warren_files.rs | 12 +- .../handlers/warrens/upload_warren_files.rs | 12 +- backend/src/lib/outbound/file_system.rs | 41 ++++- .../src/lib/outbound/metrics_debug_logger.rs | 9 +- .../src/lib/outbound/notifier_debug_logger.rs | 14 +- 26 files changed, 576 insertions(+), 439 deletions(-) delete mode 100644 backend/src/lib/domain/file_system/mod.rs delete mode 100644 backend/src/lib/domain/file_system/models/mod.rs delete mode 100644 backend/src/lib/domain/file_system/ports.rs rename backend/src/lib/domain/{file_system/models/file.rs => warren/models/file/mod.rs} (67%) create mode 100644 backend/src/lib/domain/warren/models/file/requests.rs create mode 100644 backend/src/lib/domain/warren/models/warren/mod.rs rename backend/src/lib/domain/warren/models/{warren.rs => warren/requests.rs} (87%) delete mode 100644 backend/src/lib/domain/warren/ports.rs create mode 100644 backend/src/lib/domain/warren/ports/metrics.rs create mode 100644 backend/src/lib/domain/warren/ports/mod.rs create mode 100644 backend/src/lib/domain/warren/ports/notifier.rs create mode 100644 backend/src/lib/domain/warren/ports/repository.rs rename backend/src/lib/domain/{file_system/service.rs => warren/service/file_system.rs} (80%) create mode 100644 backend/src/lib/domain/warren/service/mod.rs rename backend/src/lib/domain/warren/{service.rs => service/warren.rs} (94%) diff --git a/backend/src/bin/backend/main.rs b/backend/src/bin/backend/main.rs index 939aaa1..a36abf1 100644 --- a/backend/src/bin/backend/main.rs +++ b/backend/src/bin/backend/main.rs @@ -32,10 +32,14 @@ async fn main() -> anyhow::Result<()> { let fs_config = FileSystemConfig::new(config.serve_dir); let fs = FileSystem::new(fs_config)?; - let fs_service = domain::file_system::service::Service::new(fs, metrics, notifier); + let fs_service = domain::warren::service::file_system::Service::new(fs, metrics, notifier); - let warren_service = - domain::warren::service::Service::new(postgres, metrics, notifier, fs_service.clone()); + let warren_service = domain::warren::service::warren::Service::new( + postgres, + metrics, + notifier, + fs_service.clone(), + ); let server_config = HttpServerConfig::new( &config.server_address, diff --git a/backend/src/lib/domain/file_system/mod.rs b/backend/src/lib/domain/file_system/mod.rs deleted file mode 100644 index 901e625..0000000 --- a/backend/src/lib/domain/file_system/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod models; -pub mod ports; -pub mod service; diff --git a/backend/src/lib/domain/file_system/models/mod.rs b/backend/src/lib/domain/file_system/models/mod.rs deleted file mode 100644 index 2e172cd..0000000 --- a/backend/src/lib/domain/file_system/models/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod file; diff --git a/backend/src/lib/domain/file_system/ports.rs b/backend/src/lib/domain/file_system/ports.rs deleted file mode 100644 index 75e18d8..0000000 --- a/backend/src/lib/domain/file_system/ports.rs +++ /dev/null @@ -1,80 +0,0 @@ -use super::models::file::{ - CreateDirectoryError, CreateDirectoryRequest, CreateFileError, CreateFileRequest, - DeleteDirectoryError, DeleteDirectoryRequest, DeleteFileError, DeleteFileRequest, File, - FilePath, ListFilesError, ListFilesRequest, -}; - -pub trait FileSystemService: Clone + Send + Sync + 'static { - fn list_files( - &self, - request: ListFilesRequest, - ) -> impl Future, ListFilesError>> + Send; - - fn create_directory( - &self, - request: CreateDirectoryRequest, - ) -> impl Future> + Send; - fn delete_directory( - &self, - request: DeleteDirectoryRequest, - ) -> impl Future> + Send; - - fn create_file( - &self, - request: CreateFileRequest, - ) -> impl Future> + Send; - fn delete_file( - &self, - request: DeleteFileRequest, - ) -> impl Future> + Send; -} - -pub trait FileSystemRepository: Clone + Send + Sync + 'static { - fn list_files( - &self, - request: ListFilesRequest, - ) -> impl Future, ListFilesError>> + Send; - - fn create_directory( - &self, - request: CreateDirectoryRequest, - ) -> impl Future> + Send; - fn delete_directory( - &self, - request: DeleteDirectoryRequest, - ) -> impl Future> + Send; - - fn create_file( - &self, - request: CreateFileRequest, - ) -> impl Future> + Send; - fn delete_file( - &self, - request: DeleteFileRequest, - ) -> impl Future> + Send; -} - -pub trait FileSystemMetrics: Clone + Send + Sync + 'static { - fn record_list_files_success(&self) -> impl Future + Send; - fn record_list_files_failure(&self) -> impl Future + Send; - - fn record_directory_creation_success(&self) -> impl Future + Send; - fn record_directory_creation_failure(&self) -> impl Future + Send; - fn record_directory_deletion_success(&self) -> impl Future + Send; - fn record_directory_deletion_failure(&self) -> impl Future + Send; - - fn record_file_creation_success(&self) -> impl Future + Send; - fn record_file_creation_failure(&self) -> impl Future + Send; - fn record_file_deletion_success(&self) -> impl Future + Send; - fn record_file_deletion_failure(&self) -> impl Future + Send; -} - -pub trait FileSystemNotifier: Clone + Send + Sync + 'static { - fn files_listed(&self, files: &Vec) -> impl Future + Send; - - fn directory_created(&self, path: &FilePath) -> impl Future + Send; - fn directory_deleted(&self, path: &FilePath) -> impl Future + Send; - - fn file_created(&self, path: &FilePath) -> impl Future + Send; - fn file_deleted(&self, path: &FilePath) -> impl Future + Send; -} diff --git a/backend/src/lib/domain/mod.rs b/backend/src/lib/domain/mod.rs index a098c04..66a8f3b 100644 --- a/backend/src/lib/domain/mod.rs +++ b/backend/src/lib/domain/mod.rs @@ -1,2 +1 @@ -pub mod file_system; pub mod warren; diff --git a/backend/src/lib/domain/file_system/models/file.rs b/backend/src/lib/domain/warren/models/file/mod.rs similarity index 67% rename from backend/src/lib/domain/file_system/models/file.rs rename to backend/src/lib/domain/warren/models/file/mod.rs index e347cdd..2e245b9 100644 --- a/backend/src/lib/domain/file_system/models/file.rs +++ b/backend/src/lib/domain/warren/models/file/mod.rs @@ -1,4 +1,7 @@ -use std::{fmt::Display, path::Path}; +mod requests; +pub use requests::*; + +use std::path::Path; use derive_more::Display; use serde::Serialize; @@ -70,25 +73,20 @@ impl FileName { Ok(Self(trimmed.to_owned())) } + + pub fn as_str(&self) -> &str { + &self.0 + } } /// A valid file type -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Display)] #[serde(rename_all = "camelCase")] pub enum FileType { File, Directory, } -impl Display for FileType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - Self::File => "File", - Self::Directory => "Directory", - }) - } -} - /// A valid file mime type #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] pub struct FileMimeType(String); @@ -229,115 +227,3 @@ impl From for FilePath { Self(value.0) } } - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct ListFilesRequest { - path: AbsoluteFilePath, -} - -impl ListFilesRequest { - pub fn new(path: AbsoluteFilePath) -> Self { - Self { path } - } - - pub fn path(&self) -> &AbsoluteFilePath { - &self.path - } -} - -#[derive(Debug, Error)] -pub enum ListFilesError { - #[error("Directory at path {0} does not exist")] - NotFound(String), - #[error(transparent)] - Unknown(#[from] anyhow::Error), -} - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeleteDirectoryRequest { - path: AbsoluteFilePath, -} - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeleteFileRequest { - path: AbsoluteFilePath, -} - -impl DeleteDirectoryRequest { - pub fn new(path: AbsoluteFilePath) -> Self { - Self { path } - } - - pub fn path(&self) -> &AbsoluteFilePath { - &self.path - } -} - -impl DeleteFileRequest { - pub fn new(path: AbsoluteFilePath) -> Self { - Self { path } - } - - pub fn path(&self) -> &AbsoluteFilePath { - &self.path - } -} - -#[derive(Debug, Error)] -pub enum DeleteDirectoryError { - #[error(transparent)] - Unknown(#[from] anyhow::Error), -} - -#[derive(Debug, Error)] -pub enum DeleteFileError { - #[error(transparent)] - Unknown(#[from] anyhow::Error), -} - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct CreateDirectoryRequest { - path: AbsoluteFilePath, -} - -impl CreateDirectoryRequest { - pub fn new(path: AbsoluteFilePath) -> Self { - Self { path } - } - - pub fn path(&self) -> &AbsoluteFilePath { - &self.path - } -} - -#[derive(Debug, Error)] -pub enum CreateDirectoryError { - #[error(transparent)] - Unknown(#[from] anyhow::Error), -} - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct CreateFileRequest { - path: AbsoluteFilePath, - data: Box<[u8]>, -} - -impl CreateFileRequest { - pub fn new(path: AbsoluteFilePath, data: Box<[u8]>) -> Self { - Self { path, data } - } - - pub fn path(&self) -> &AbsoluteFilePath { - &self.path - } - - pub fn data(&self) -> &[u8] { - &self.data - } -} - -#[derive(Debug, Error)] -pub enum CreateFileError { - #[error(transparent)] - Unknown(#[from] anyhow::Error), -} diff --git a/backend/src/lib/domain/warren/models/file/requests.rs b/backend/src/lib/domain/warren/models/file/requests.rs new file mode 100644 index 0000000..e1a9d36 --- /dev/null +++ b/backend/src/lib/domain/warren/models/file/requests.rs @@ -0,0 +1,141 @@ +use thiserror::Error; + +use super::{AbsoluteFilePath, FileName}; + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ListFilesRequest { + path: AbsoluteFilePath, +} + +impl ListFilesRequest { + pub fn new(path: AbsoluteFilePath) -> Self { + Self { path } + } + + pub fn path(&self) -> &AbsoluteFilePath { + &self.path + } +} + +#[derive(Debug, Error)] +pub enum ListFilesError { + #[error("Directory at path {0} does not exist")] + NotFound(String), + #[error(transparent)] + Unknown(#[from] anyhow::Error), +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct DeleteDirectoryRequest { + path: AbsoluteFilePath, +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct DeleteFileRequest { + path: AbsoluteFilePath, +} + +impl DeleteDirectoryRequest { + pub fn new(path: AbsoluteFilePath) -> Self { + Self { path } + } + + pub fn path(&self) -> &AbsoluteFilePath { + &self.path + } +} + +impl DeleteFileRequest { + pub fn new(path: AbsoluteFilePath) -> Self { + Self { path } + } + + pub fn path(&self) -> &AbsoluteFilePath { + &self.path + } +} + +#[derive(Debug, Error)] +pub enum DeleteDirectoryError { + #[error(transparent)] + Unknown(#[from] anyhow::Error), +} + +#[derive(Debug, Error)] +pub enum DeleteFileError { + #[error(transparent)] + Unknown(#[from] anyhow::Error), +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct CreateDirectoryRequest { + path: AbsoluteFilePath, +} + +impl CreateDirectoryRequest { + pub fn new(path: AbsoluteFilePath) -> Self { + Self { path } + } + + pub fn path(&self) -> &AbsoluteFilePath { + &self.path + } +} + +#[derive(Debug, Error)] +pub enum CreateDirectoryError { + #[error(transparent)] + Unknown(#[from] anyhow::Error), +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct CreateFileRequest { + path: AbsoluteFilePath, + data: Box<[u8]>, +} + +impl CreateFileRequest { + pub fn new(path: AbsoluteFilePath, data: Box<[u8]>) -> Self { + Self { path, data } + } + + pub fn path(&self) -> &AbsoluteFilePath { + &self.path + } + + pub fn data(&self) -> &[u8] { + &self.data + } +} + +#[derive(Debug, Error)] +pub enum CreateFileError { + #[error(transparent)] + Unknown(#[from] anyhow::Error), +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct RenameEntryRequest { + path: AbsoluteFilePath, + new_name: FileName, +} + +impl RenameEntryRequest { + pub fn new(path: AbsoluteFilePath, new_name: FileName) -> Self { + Self { path, new_name } + } + + pub fn path(&self) -> &AbsoluteFilePath { + &self.path + } + + pub fn new_name(&self) -> &FileName { + &self.new_name + } +} + +#[derive(Debug, Error)] +pub enum RenameEntryError { + #[error(transparent)] + Unknown(#[from] anyhow::Error), +} diff --git a/backend/src/lib/domain/warren/models/mod.rs b/backend/src/lib/domain/warren/models/mod.rs index 66a8f3b..8078362 100644 --- a/backend/src/lib/domain/warren/models/mod.rs +++ b/backend/src/lib/domain/warren/models/mod.rs @@ -1 +1,2 @@ +pub mod file; pub mod warren; diff --git a/backend/src/lib/domain/warren/models/warren/mod.rs b/backend/src/lib/domain/warren/models/warren/mod.rs new file mode 100644 index 0000000..281f0d6 --- /dev/null +++ b/backend/src/lib/domain/warren/models/warren/mod.rs @@ -0,0 +1,55 @@ +mod requests; +use derive_more::Display; +pub use requests::*; +use thiserror::Error; +use uuid::Uuid; + +use super::file::AbsoluteFilePath; + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, sqlx::FromRow)] +pub struct Warren { + id: Uuid, + name: WarrenName, + path: AbsoluteFilePath, +} + +impl Warren { + pub fn new(id: Uuid, name: WarrenName, path: AbsoluteFilePath) -> Self { + Self { id, name, path } + } + + pub fn id(&self) -> &Uuid { + &self.id + } + + pub fn name(&self) -> &WarrenName { + &self.name + } + + pub fn path(&self) -> &AbsoluteFilePath { + &self.path + } +} + +/// A valid warren name +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display, sqlx::Type)] +#[sqlx(transparent)] +pub struct WarrenName(String); + +#[derive(Clone, Debug, Error)] +pub enum WarrenNameError { + #[error("A warren name must not be empty")] + Empty, +} + +impl WarrenName { + pub fn new(raw: &str) -> Result { + let trimmed = raw.trim(); + + if trimmed.is_empty() { + return Err(WarrenNameError::Empty); + } + + Ok(Self(trimmed.to_owned())) + } +} diff --git a/backend/src/lib/domain/warren/models/warren.rs b/backend/src/lib/domain/warren/models/warren/requests.rs similarity index 87% rename from backend/src/lib/domain/warren/models/warren.rs rename to backend/src/lib/domain/warren/models/warren/requests.rs index 9a5cd46..a3e274a 100644 --- a/backend/src/lib/domain/warren/models/warren.rs +++ b/backend/src/lib/domain/warren/models/warren/requests.rs @@ -1,60 +1,13 @@ -use derive_more::Display; use thiserror::Error; use uuid::Uuid; -use crate::domain::file_system::models::file::{ +use crate::domain::warren::models::file::{ AbsoluteFilePath, CreateDirectoryError, CreateDirectoryRequest, CreateFileError, CreateFileRequest, DeleteDirectoryError, DeleteDirectoryRequest, DeleteFileError, DeleteFileRequest, FileName, FilePath, ListFilesError, ListFilesRequest, RelativeFilePath, }; -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, sqlx::FromRow)] -pub struct Warren { - id: Uuid, - name: WarrenName, - path: AbsoluteFilePath, -} - -impl Warren { - pub fn new(id: Uuid, name: WarrenName, path: AbsoluteFilePath) -> Self { - Self { id, name, path } - } - - pub fn id(&self) -> &Uuid { - &self.id - } - - pub fn name(&self) -> &WarrenName { - &self.name - } - - pub fn path(&self) -> &AbsoluteFilePath { - &self.path - } -} - -/// A valid warren name -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display, sqlx::Type)] -#[sqlx(transparent)] -pub struct WarrenName(String); - -#[derive(Clone, Debug, Error)] -pub enum WarrenNameError { - #[error("A warren name must not be empty")] - Empty, -} - -impl WarrenName { - pub fn new(raw: &str) -> Result { - let trimmed = raw.trim(); - - if trimmed.is_empty() { - return Err(WarrenNameError::Empty); - } - - Ok(Self(trimmed.to_owned())) - } -} +use super::Warren; #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct FetchWarrenRequest { diff --git a/backend/src/lib/domain/warren/ports.rs b/backend/src/lib/domain/warren/ports.rs deleted file mode 100644 index 3d50b15..0000000 --- a/backend/src/lib/domain/warren/ports.rs +++ /dev/null @@ -1,129 +0,0 @@ -use crate::domain::file_system::models::file::{File, FilePath}; - -use super::models::warren::{ - CreateWarrenDirectoryError, CreateWarrenDirectoryRequest, DeleteWarrenDirectoryError, - DeleteWarrenDirectoryRequest, DeleteWarrenFileError, DeleteWarrenFileRequest, FetchWarrenError, - FetchWarrenRequest, ListWarrenFilesError, ListWarrenFilesRequest, ListWarrensError, - ListWarrensRequest, UploadWarrenFilesError, UploadWarrenFilesRequest, Warren, -}; - -pub trait WarrenService: Clone + Send + Sync + 'static { - fn list_warrens( - &self, - request: ListWarrensRequest, - ) -> impl Future, ListWarrensError>> + Send; - - fn fetch_warren( - &self, - request: FetchWarrenRequest, - ) -> impl Future> + Send; - - fn list_files( - &self, - request: ListWarrenFilesRequest, - ) -> impl Future, ListWarrenFilesError>> + Send; - - fn create_warren_directory( - &self, - request: CreateWarrenDirectoryRequest, - ) -> impl Future> + Send; - - fn delete_warren_directory( - &self, - request: DeleteWarrenDirectoryRequest, - ) -> impl Future> + Send; - - fn upload_warren_files( - &self, - request: UploadWarrenFilesRequest, - ) -> impl Future, UploadWarrenFilesError>> + Send; - fn delete_warren_file( - &self, - request: DeleteWarrenFileRequest, - ) -> impl Future> + Send; -} - -pub trait WarrenRepository: Clone + Send + Sync + 'static { - fn list_warrens( - &self, - request: ListWarrensRequest, - ) -> impl Future, ListWarrensError>> + Send; - - fn fetch_warren( - &self, - request: FetchWarrenRequest, - ) -> impl Future> + Send; -} - -pub trait WarrenMetrics: Clone + Send + Sync + 'static { - fn record_warren_list_success(&self) -> impl Future + Send; - fn record_warren_list_failure(&self) -> impl Future + Send; - - fn record_warren_fetch_success(&self) -> impl Future + Send; - fn record_warren_fetch_failure(&self) -> impl Future + Send; - - fn record_list_warren_files_success(&self) -> impl Future + Send; - fn record_list_warren_files_failure(&self) -> impl Future + Send; - - fn record_warren_directory_creation_success(&self) -> impl Future + Send; - fn record_warren_directory_creation_failure(&self) -> impl Future + Send; - - fn record_warren_directory_deletion_success(&self) -> impl Future + Send; - fn record_warren_directory_deletion_failure(&self) -> impl Future + Send; - - /// A single file upload succeeded - fn record_warren_file_upload_success(&self) -> impl Future + Send; - /// A single file upload failed - fn record_warren_file_upload_failure(&self) -> impl Future + Send; - - /// An upload succeeded fully - fn record_warren_files_upload_success(&self) -> impl Future + Send; - /// An upload failed at least partially - fn record_warren_files_upload_failure(&self) -> impl Future + Send; - - fn record_warren_file_deletion_success(&self) -> impl Future + Send; - fn record_warren_file_deletion_failure(&self) -> impl Future + Send; -} - -pub trait WarrenNotifier: Clone + Send + Sync + 'static { - fn warrens_listed(&self, warrens: &Vec) -> impl Future + Send; - fn warren_fetched(&self, warren: &Warren) -> impl Future + Send; - fn warren_files_listed( - &self, - warren: &Warren, - files: &Vec, - ) -> impl Future + Send; - fn warren_directory_created( - &self, - warren: &Warren, - path: &FilePath, - ) -> impl Future + Send; - fn warren_directory_deleted( - &self, - warren: &Warren, - path: &FilePath, - ) -> impl Future + Send; - /// A single file was uploaded - /// - /// * `warren`: The warren the file was uploaded to - /// * `path`: The file's path - fn warren_file_uploaded( - &self, - warren: &Warren, - path: &FilePath, - ) -> impl Future + Send; - /// A collection of files was uploaded - /// - /// * `warren`: The warren the file was uploaded to - /// * `files`: The files' paths - fn warren_files_uploaded( - &self, - warren: &Warren, - files: &[FilePath], - ) -> impl Future + Send; - fn warren_file_deleted( - &self, - warren: &Warren, - path: &FilePath, - ) -> impl Future + Send; -} diff --git a/backend/src/lib/domain/warren/ports/metrics.rs b/backend/src/lib/domain/warren/ports/metrics.rs new file mode 100644 index 0000000..76e3a93 --- /dev/null +++ b/backend/src/lib/domain/warren/ports/metrics.rs @@ -0,0 +1,47 @@ +pub trait WarrenMetrics: Clone + Send + Sync + 'static { + fn record_warren_list_success(&self) -> impl Future + Send; + fn record_warren_list_failure(&self) -> impl Future + Send; + + fn record_warren_fetch_success(&self) -> impl Future + Send; + fn record_warren_fetch_failure(&self) -> impl Future + Send; + + fn record_list_warren_files_success(&self) -> impl Future + Send; + fn record_list_warren_files_failure(&self) -> impl Future + Send; + + fn record_warren_directory_creation_success(&self) -> impl Future + Send; + fn record_warren_directory_creation_failure(&self) -> impl Future + Send; + + fn record_warren_directory_deletion_success(&self) -> impl Future + Send; + fn record_warren_directory_deletion_failure(&self) -> impl Future + Send; + + /// A single file upload succeeded + fn record_warren_file_upload_success(&self) -> impl Future + Send; + /// A single file upload failed + fn record_warren_file_upload_failure(&self) -> impl Future + Send; + + /// An upload succeeded fully + fn record_warren_files_upload_success(&self) -> impl Future + Send; + /// An upload failed at least partially + fn record_warren_files_upload_failure(&self) -> impl Future + Send; + + fn record_warren_file_deletion_success(&self) -> impl Future + Send; + fn record_warren_file_deletion_failure(&self) -> impl Future + Send; +} + +pub trait FileSystemMetrics: Clone + Send + Sync + 'static { + fn record_list_files_success(&self) -> impl Future + Send; + fn record_list_files_failure(&self) -> impl Future + Send; + + fn record_directory_creation_success(&self) -> impl Future + Send; + fn record_directory_creation_failure(&self) -> impl Future + Send; + fn record_directory_deletion_success(&self) -> impl Future + Send; + fn record_directory_deletion_failure(&self) -> impl Future + Send; + + fn record_file_creation_success(&self) -> impl Future + Send; + fn record_file_creation_failure(&self) -> impl Future + Send; + fn record_file_deletion_success(&self) -> impl Future + Send; + fn record_file_deletion_failure(&self) -> impl Future + Send; + + fn record_entry_rename_success(&self) -> impl Future + Send; + fn record_entry_rename_failure(&self) -> impl Future + Send; +} diff --git a/backend/src/lib/domain/warren/ports/mod.rs b/backend/src/lib/domain/warren/ports/mod.rs new file mode 100644 index 0000000..7b79fba --- /dev/null +++ b/backend/src/lib/domain/warren/ports/mod.rs @@ -0,0 +1,88 @@ +mod metrics; +mod notifier; +mod repository; + +pub use metrics::*; +pub use notifier::*; +pub use repository::*; + +use super::models::{ + file::{ + CreateDirectoryError, CreateDirectoryRequest, CreateFileError, CreateFileRequest, + DeleteDirectoryError, DeleteDirectoryRequest, DeleteFileError, DeleteFileRequest, File, + FilePath, ListFilesError, ListFilesRequest, RenameEntryError, RenameEntryRequest, + }, + warren::{ + CreateWarrenDirectoryError, CreateWarrenDirectoryRequest, DeleteWarrenDirectoryError, + DeleteWarrenDirectoryRequest, DeleteWarrenFileError, DeleteWarrenFileRequest, + FetchWarrenError, FetchWarrenRequest, ListWarrenFilesError, ListWarrenFilesRequest, + ListWarrensError, ListWarrensRequest, UploadWarrenFilesError, UploadWarrenFilesRequest, + Warren, + }, +}; + +pub trait WarrenService: Clone + Send + Sync + 'static { + fn list_warrens( + &self, + request: ListWarrensRequest, + ) -> impl Future, ListWarrensError>> + Send; + + fn fetch_warren( + &self, + request: FetchWarrenRequest, + ) -> impl Future> + Send; + + fn list_files( + &self, + request: ListWarrenFilesRequest, + ) -> impl Future, ListWarrenFilesError>> + Send; + + fn create_warren_directory( + &self, + request: CreateWarrenDirectoryRequest, + ) -> impl Future> + Send; + + fn delete_warren_directory( + &self, + request: DeleteWarrenDirectoryRequest, + ) -> impl Future> + Send; + + fn upload_warren_files( + &self, + request: UploadWarrenFilesRequest, + ) -> impl Future, UploadWarrenFilesError>> + Send; + fn delete_warren_file( + &self, + request: DeleteWarrenFileRequest, + ) -> impl Future> + Send; +} + +pub trait FileSystemService: Clone + Send + Sync + 'static { + fn list_files( + &self, + request: ListFilesRequest, + ) -> impl Future, ListFilesError>> + Send; + + fn create_directory( + &self, + request: CreateDirectoryRequest, + ) -> impl Future> + Send; + fn delete_directory( + &self, + request: DeleteDirectoryRequest, + ) -> impl Future> + Send; + + fn create_file( + &self, + request: CreateFileRequest, + ) -> impl Future> + Send; + fn delete_file( + &self, + request: DeleteFileRequest, + ) -> impl Future> + Send; + + fn rename_entry( + &self, + request: RenameEntryRequest, + ) -> impl Future> + Send; +} diff --git a/backend/src/lib/domain/warren/ports/notifier.rs b/backend/src/lib/domain/warren/ports/notifier.rs new file mode 100644 index 0000000..c790a83 --- /dev/null +++ b/backend/src/lib/domain/warren/ports/notifier.rs @@ -0,0 +1,63 @@ +use crate::domain::warren::models::{ + file::{File, FilePath}, + warren::Warren, +}; + +pub trait WarrenNotifier: Clone + Send + Sync + 'static { + fn warrens_listed(&self, warrens: &Vec) -> impl Future + Send; + fn warren_fetched(&self, warren: &Warren) -> impl Future + Send; + fn warren_files_listed( + &self, + warren: &Warren, + files: &Vec, + ) -> impl Future + Send; + fn warren_directory_created( + &self, + warren: &Warren, + path: &FilePath, + ) -> impl Future + Send; + fn warren_directory_deleted( + &self, + warren: &Warren, + path: &FilePath, + ) -> impl Future + Send; + /// A single file was uploaded + /// + /// * `warren`: The warren the file was uploaded to + /// * `path`: The file's path + fn warren_file_uploaded( + &self, + warren: &Warren, + path: &FilePath, + ) -> impl Future + Send; + /// A collection of files was uploaded + /// + /// * `warren`: The warren the file was uploaded to + /// * `files`: The files' paths + fn warren_files_uploaded( + &self, + warren: &Warren, + files: &[FilePath], + ) -> impl Future + Send; + fn warren_file_deleted( + &self, + warren: &Warren, + path: &FilePath, + ) -> impl Future + Send; +} + +pub trait FileSystemNotifier: Clone + Send + Sync + 'static { + fn files_listed(&self, files: &Vec) -> impl Future + Send; + + fn directory_created(&self, path: &FilePath) -> impl Future + Send; + fn directory_deleted(&self, path: &FilePath) -> impl Future + Send; + + fn file_created(&self, path: &FilePath) -> impl Future + Send; + fn file_deleted(&self, path: &FilePath) -> impl Future + Send; + + fn entry_renamed( + &self, + old_path: &FilePath, + new_path: &FilePath, + ) -> impl Future + Send; +} diff --git a/backend/src/lib/domain/warren/ports/repository.rs b/backend/src/lib/domain/warren/ports/repository.rs new file mode 100644 index 0000000..68bddc8 --- /dev/null +++ b/backend/src/lib/domain/warren/ports/repository.rs @@ -0,0 +1,50 @@ +use crate::domain::warren::models::{ + file::{ + CreateDirectoryError, CreateDirectoryRequest, CreateFileError, CreateFileRequest, + DeleteDirectoryError, DeleteDirectoryRequest, DeleteFileError, DeleteFileRequest, File, + FilePath, ListFilesError, ListFilesRequest, RenameEntryError, RenameEntryRequest, + }, + warren::{FetchWarrenError, FetchWarrenRequest, ListWarrensError, ListWarrensRequest, Warren}, +}; + +pub trait WarrenRepository: Clone + Send + Sync + 'static { + fn list_warrens( + &self, + request: ListWarrensRequest, + ) -> impl Future, ListWarrensError>> + Send; + + fn fetch_warren( + &self, + request: FetchWarrenRequest, + ) -> impl Future> + Send; +} + +pub trait FileSystemRepository: Clone + Send + Sync + 'static { + fn list_files( + &self, + request: ListFilesRequest, + ) -> impl Future, ListFilesError>> + Send; + + fn create_directory( + &self, + request: CreateDirectoryRequest, + ) -> impl Future> + Send; + fn delete_directory( + &self, + request: DeleteDirectoryRequest, + ) -> impl Future> + Send; + + fn create_file( + &self, + request: CreateFileRequest, + ) -> impl Future> + Send; + fn delete_file( + &self, + request: DeleteFileRequest, + ) -> impl Future> + Send; + + fn rename_entry( + &self, + request: RenameEntryRequest, + ) -> impl Future> + Send; +} diff --git a/backend/src/lib/domain/file_system/service.rs b/backend/src/lib/domain/warren/service/file_system.rs similarity index 80% rename from backend/src/lib/domain/file_system/service.rs rename to backend/src/lib/domain/warren/service/file_system.rs index bc0225b..2179b61 100644 --- a/backend/src/lib/domain/file_system/service.rs +++ b/backend/src/lib/domain/warren/service/file_system.rs @@ -1,8 +1,8 @@ -use super::{ +use crate::domain::warren::{ models::file::{ CreateDirectoryError, CreateDirectoryRequest, CreateFileError, CreateFileRequest, - DeleteDirectoryError, DeleteDirectoryRequest, DeleteFileError, DeleteFileRequest, FilePath, - ListFilesError, ListFilesRequest, + DeleteDirectoryError, DeleteDirectoryRequest, DeleteFileError, DeleteFileRequest, File, + FilePath, ListFilesError, ListFilesRequest, RenameEntryError, RenameEntryRequest, }, ports::{FileSystemMetrics, FileSystemNotifier, FileSystemRepository, FileSystemService}, }; @@ -40,10 +40,7 @@ where M: FileSystemMetrics, N: FileSystemNotifier, { - async fn list_files( - &self, - request: ListFilesRequest, - ) -> Result, ListFilesError> { + async fn list_files(&self, request: ListFilesRequest) -> Result, ListFilesError> { let result = self.repository.list_files(request).await; if let Ok(files) = result.as_ref() { @@ -113,4 +110,21 @@ where result } + + async fn rename_entry( + &self, + request: RenameEntryRequest, + ) -> Result { + let old_path = request.path().clone(); + let result = self.repository.rename_entry(request).await; + + if let Ok(path) = result.as_ref() { + self.metrics.record_entry_rename_success().await; + self.notifier.entry_renamed(&old_path.into(), path).await; + } else { + self.metrics.record_entry_rename_failure().await; + } + + result + } } diff --git a/backend/src/lib/domain/warren/service/mod.rs b/backend/src/lib/domain/warren/service/mod.rs new file mode 100644 index 0000000..a098c04 --- /dev/null +++ b/backend/src/lib/domain/warren/service/mod.rs @@ -0,0 +1,2 @@ +pub mod file_system; +pub mod warren; diff --git a/backend/src/lib/domain/warren/service.rs b/backend/src/lib/domain/warren/service/warren.rs similarity index 94% rename from backend/src/lib/domain/warren/service.rs rename to backend/src/lib/domain/warren/service/warren.rs index cc36160..5d4d586 100644 --- a/backend/src/lib/domain/warren/service.rs +++ b/backend/src/lib/domain/warren/service/warren.rs @@ -1,18 +1,21 @@ use anyhow::Context; -use crate::domain::file_system::{ - models::file::{File, FilePath}, +use crate::domain::warren::{ + models::{ + file::{File, FilePath}, + warren::{ListWarrensError, ListWarrensRequest}, + }, ports::FileSystemService, }; use super::{ - models::warren::{ + super::models::warren::{ CreateWarrenDirectoryError, CreateWarrenDirectoryRequest, DeleteWarrenDirectoryError, DeleteWarrenDirectoryRequest, DeleteWarrenFileError, DeleteWarrenFileRequest, FetchWarrenError, FetchWarrenRequest, ListWarrenFilesError, ListWarrenFilesRequest, UploadWarrenFilesError, UploadWarrenFilesRequest, Warren, }, - ports::{WarrenMetrics, WarrenNotifier, WarrenRepository, WarrenService}, + super::ports::{WarrenMetrics, WarrenNotifier, WarrenRepository, WarrenService}, }; #[derive(Debug, Clone)] @@ -55,8 +58,8 @@ where { async fn list_warrens( &self, - request: super::models::warren::ListWarrensRequest, - ) -> Result, super::models::warren::ListWarrensError> { + request: ListWarrensRequest, + ) -> Result, ListWarrensError> { let result = self.repository.list_warrens(request).await; if let Ok(warren) = result.as_ref() { diff --git a/backend/src/lib/inbound/http/handlers/warrens/create_warren_directory.rs b/backend/src/lib/inbound/http/handlers/warrens/create_warren_directory.rs index 1d53676..1d39e8b 100644 --- a/backend/src/lib/inbound/http/handlers/warrens/create_warren_directory.rs +++ b/backend/src/lib/inbound/http/handlers/warrens/create_warren_directory.rs @@ -4,12 +4,12 @@ use thiserror::Error; use uuid::Uuid; use crate::{ - domain::{ - file_system::models::file::{AbsoluteFilePathError, FilePath, FilePathError}, - warren::{ - models::warren::{CreateWarrenDirectoryError, CreateWarrenDirectoryRequest}, - ports::WarrenService, + domain::warren::{ + models::{ + file::{AbsoluteFilePathError, FilePath, FilePathError}, + warren::{CreateWarrenDirectoryError, CreateWarrenDirectoryRequest}, }, + ports::WarrenService, }, inbound::http::{ AppState, diff --git a/backend/src/lib/inbound/http/handlers/warrens/delete_warren_directory.rs b/backend/src/lib/inbound/http/handlers/warrens/delete_warren_directory.rs index adcb243..31cea3e 100644 --- a/backend/src/lib/inbound/http/handlers/warrens/delete_warren_directory.rs +++ b/backend/src/lib/inbound/http/handlers/warrens/delete_warren_directory.rs @@ -4,12 +4,12 @@ use thiserror::Error; use uuid::Uuid; use crate::{ - domain::{ - file_system::models::file::{AbsoluteFilePathError, FilePath, FilePathError}, - warren::{ - models::warren::{DeleteWarrenDirectoryError, DeleteWarrenDirectoryRequest}, - ports::WarrenService, + domain::warren::{ + models::{ + file::{AbsoluteFilePathError, FilePath, FilePathError}, + warren::{DeleteWarrenDirectoryError, DeleteWarrenDirectoryRequest}, }, + ports::WarrenService, }, inbound::http::{ AppState, diff --git a/backend/src/lib/inbound/http/handlers/warrens/delete_warren_file.rs b/backend/src/lib/inbound/http/handlers/warrens/delete_warren_file.rs index cf93610..8e27ca2 100644 --- a/backend/src/lib/inbound/http/handlers/warrens/delete_warren_file.rs +++ b/backend/src/lib/inbound/http/handlers/warrens/delete_warren_file.rs @@ -4,12 +4,12 @@ use thiserror::Error; use uuid::Uuid; use crate::{ - domain::{ - file_system::models::file::{AbsoluteFilePathError, FilePath, FilePathError}, - warren::{ - models::warren::{DeleteWarrenFileError, DeleteWarrenFileRequest}, - ports::WarrenService, + domain::warren::{ + models::{ + file::{AbsoluteFilePathError, FilePath, FilePathError}, + warren::{DeleteWarrenFileError, DeleteWarrenFileRequest}, }, + ports::WarrenService, }, inbound::http::{ AppState, diff --git a/backend/src/lib/inbound/http/handlers/warrens/list_warren_files.rs b/backend/src/lib/inbound/http/handlers/warrens/list_warren_files.rs index 673eff8..d9a77b9 100644 --- a/backend/src/lib/inbound/http/handlers/warrens/list_warren_files.rs +++ b/backend/src/lib/inbound/http/handlers/warrens/list_warren_files.rs @@ -4,14 +4,12 @@ use thiserror::Error; use uuid::Uuid; use crate::{ - domain::{ - file_system::models::file::{ - AbsoluteFilePathError, File, FileMimeType, FilePath, FilePathError, FileType, - }, - warren::{ - models::warren::{ListWarrenFilesError, ListWarrenFilesRequest}, - ports::WarrenService, + domain::warren::{ + models::{ + file::{AbsoluteFilePathError, File, FileMimeType, FilePath, FilePathError, FileType}, + warren::{ListWarrenFilesError, ListWarrenFilesRequest}, }, + ports::WarrenService, }, inbound::http::{ AppState, diff --git a/backend/src/lib/inbound/http/handlers/warrens/upload_warren_files.rs b/backend/src/lib/inbound/http/handlers/warrens/upload_warren_files.rs index 37f1dac..1d4aff2 100644 --- a/backend/src/lib/inbound/http/handlers/warrens/upload_warren_files.rs +++ b/backend/src/lib/inbound/http/handlers/warrens/upload_warren_files.rs @@ -4,17 +4,15 @@ use thiserror::Error; use uuid::Uuid; use crate::{ - domain::{ - file_system::models::file::{ - AbsoluteFilePathError, FileName, FileNameError, FilePath, FilePathError, - }, - warren::{ - models::warren::{ + domain::warren::{ + models::{ + file::{AbsoluteFilePathError, FileName, FileNameError, FilePath, FilePathError}, + warren::{ UploadFile, UploadFileList, UploadFileListError, UploadWarrenFilesError, UploadWarrenFilesRequest, }, - ports::WarrenService, }, + ports::WarrenService, }, inbound::http::{ AppState, diff --git a/backend/src/lib/outbound/file_system.rs b/backend/src/lib/outbound/file_system.rs index 178651c..8a793ad 100644 --- a/backend/src/lib/outbound/file_system.rs +++ b/backend/src/lib/outbound/file_system.rs @@ -3,12 +3,12 @@ use std::time::UNIX_EPOCH; use anyhow::{Context, anyhow}; use tokio::{fs, io::AsyncWriteExt as _}; -use crate::domain::file_system::{ +use crate::domain::warren::{ models::file::{ AbsoluteFilePath, CreateDirectoryError, CreateDirectoryRequest, CreateFileError, CreateFileRequest, DeleteDirectoryError, DeleteDirectoryRequest, DeleteFileError, DeleteFileRequest, File, FileMimeType, FileName, FilePath, FileType, ListFilesError, - ListFilesRequest, + ListFilesRequest, RenameEntryError, RenameEntryRequest, }, ports::FileSystemRepository, }; @@ -139,6 +139,27 @@ impl FileSystem { Ok(path) } + + async fn rename( + &self, + path: &AbsoluteFilePath, + new_name: &FileName, + ) -> anyhow::Result { + let current_path = self.get_target_path(path); + + let new_path = { + let mut c = current_path.to_string(); + let last_slash_index = c.rfind('/').unwrap(); + c.drain((last_slash_index + 1)..); + c.push_str(new_name.as_str()); + + FilePath::new(&c)? + }; + + fs::rename(current_path, &new_path).await?; + + Ok(new_path) + } } impl FileSystemRepository for FileSystem { @@ -198,4 +219,20 @@ impl FileSystemRepository for FileSystem { Ok(deleted_path) } + + async fn rename_entry( + &self, + request: RenameEntryRequest, + ) -> Result { + let new_path = self + .rename(request.path(), request.new_name()) + .await + .context(format!( + "Failed to rename {} to {}", + request.path(), + request.new_name() + ))?; + + Ok(new_path) + } } diff --git a/backend/src/lib/outbound/metrics_debug_logger.rs b/backend/src/lib/outbound/metrics_debug_logger.rs index 51e31f7..35439d8 100644 --- a/backend/src/lib/outbound/metrics_debug_logger.rs +++ b/backend/src/lib/outbound/metrics_debug_logger.rs @@ -1,4 +1,4 @@ -use crate::domain::{file_system::ports::FileSystemMetrics, warren::ports::WarrenMetrics}; +use crate::domain::warren::ports::{FileSystemMetrics, WarrenMetrics}; #[derive(Debug, Clone, Copy)] pub struct MetricsDebugLogger; @@ -107,4 +107,11 @@ impl FileSystemMetrics for MetricsDebugLogger { async fn record_file_deletion_failure(&self) { log::debug!("[Metrics] File deletion failed"); } + + async fn record_entry_rename_success(&self) -> () { + log::debug!("[Metrics] Entry rename succeeded"); + } + async fn record_entry_rename_failure(&self) -> () { + log::debug!("[Metrics] Entry rename failed"); + } } diff --git a/backend/src/lib/outbound/notifier_debug_logger.rs b/backend/src/lib/outbound/notifier_debug_logger.rs index 6dea0f6..5b50a1b 100644 --- a/backend/src/lib/outbound/notifier_debug_logger.rs +++ b/backend/src/lib/outbound/notifier_debug_logger.rs @@ -1,9 +1,9 @@ -use crate::domain::{ - file_system::{ - models::file::{File, FilePath}, - ports::FileSystemNotifier, +use crate::domain::warren::{ + models::{ + file::{File, FilePath}, + warren::Warren, }, - warren::{models::warren::Warren, ports::WarrenNotifier}, + ports::{FileSystemNotifier, WarrenNotifier}, }; #[derive(Debug, Clone, Copy)] @@ -93,4 +93,8 @@ impl FileSystemNotifier for NotifierDebugLogger { async fn file_deleted(&self, path: &FilePath) { log::debug!("[Notifier] Deleted file {}", path); } + + async fn entry_renamed(&self, old_path: &FilePath, new_path: &FilePath) { + log::debug!("[Notifier] Renamed file {} to {}", old_path, new_path); + } }