backend refactor file_system into warren domain

This commit is contained in:
2025-07-16 05:19:17 +02:00
parent a683f44ecb
commit 476ea9f516
26 changed files with 576 additions and 439 deletions

View File

@@ -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,

View File

@@ -1,3 +0,0 @@
pub mod models;
pub mod ports;
pub mod service;

View File

@@ -1 +0,0 @@
pub mod file;

View File

@@ -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<Output = Result<Vec<File>, ListFilesError>> + Send;
fn create_directory(
&self,
request: CreateDirectoryRequest,
) -> impl Future<Output = Result<FilePath, CreateDirectoryError>> + Send;
fn delete_directory(
&self,
request: DeleteDirectoryRequest,
) -> impl Future<Output = Result<FilePath, DeleteDirectoryError>> + Send;
fn create_file(
&self,
request: CreateFileRequest,
) -> impl Future<Output = Result<FilePath, CreateFileError>> + Send;
fn delete_file(
&self,
request: DeleteFileRequest,
) -> impl Future<Output = Result<FilePath, DeleteFileError>> + Send;
}
pub trait FileSystemRepository: Clone + Send + Sync + 'static {
fn list_files(
&self,
request: ListFilesRequest,
) -> impl Future<Output = Result<Vec<File>, ListFilesError>> + Send;
fn create_directory(
&self,
request: CreateDirectoryRequest,
) -> impl Future<Output = Result<FilePath, CreateDirectoryError>> + Send;
fn delete_directory(
&self,
request: DeleteDirectoryRequest,
) -> impl Future<Output = Result<FilePath, DeleteDirectoryError>> + Send;
fn create_file(
&self,
request: CreateFileRequest,
) -> impl Future<Output = Result<FilePath, CreateFileError>> + Send;
fn delete_file(
&self,
request: DeleteFileRequest,
) -> impl Future<Output = Result<FilePath, DeleteFileError>> + Send;
}
pub trait FileSystemMetrics: Clone + Send + Sync + 'static {
fn record_list_files_success(&self) -> impl Future<Output = ()> + Send;
fn record_list_files_failure(&self) -> impl Future<Output = ()> + Send;
fn record_directory_creation_success(&self) -> impl Future<Output = ()> + Send;
fn record_directory_creation_failure(&self) -> impl Future<Output = ()> + Send;
fn record_directory_deletion_success(&self) -> impl Future<Output = ()> + Send;
fn record_directory_deletion_failure(&self) -> impl Future<Output = ()> + Send;
fn record_file_creation_success(&self) -> impl Future<Output = ()> + Send;
fn record_file_creation_failure(&self) -> impl Future<Output = ()> + Send;
fn record_file_deletion_success(&self) -> impl Future<Output = ()> + Send;
fn record_file_deletion_failure(&self) -> impl Future<Output = ()> + Send;
}
pub trait FileSystemNotifier: Clone + Send + Sync + 'static {
fn files_listed(&self, files: &Vec<File>) -> impl Future<Output = ()> + Send;
fn directory_created(&self, path: &FilePath) -> impl Future<Output = ()> + Send;
fn directory_deleted(&self, path: &FilePath) -> impl Future<Output = ()> + Send;
fn file_created(&self, path: &FilePath) -> impl Future<Output = ()> + Send;
fn file_deleted(&self, path: &FilePath) -> impl Future<Output = ()> + Send;
}

View File

@@ -1,2 +1 @@
pub mod file_system;
pub mod warren;

View File

@@ -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<AbsoluteFilePath> 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),
}

View File

@@ -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),
}

View File

@@ -1 +1,2 @@
pub mod file;
pub mod warren;

View File

@@ -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<Self, WarrenNameError> {
let trimmed = raw.trim();
if trimmed.is_empty() {
return Err(WarrenNameError::Empty);
}
Ok(Self(trimmed.to_owned()))
}
}

View File

@@ -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<Self, WarrenNameError> {
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 {

View File

@@ -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<Output = Result<Vec<Warren>, ListWarrensError>> + Send;
fn fetch_warren(
&self,
request: FetchWarrenRequest,
) -> impl Future<Output = Result<Warren, FetchWarrenError>> + Send;
fn list_files(
&self,
request: ListWarrenFilesRequest,
) -> impl Future<Output = Result<Vec<File>, ListWarrenFilesError>> + Send;
fn create_warren_directory(
&self,
request: CreateWarrenDirectoryRequest,
) -> impl Future<Output = Result<FilePath, CreateWarrenDirectoryError>> + Send;
fn delete_warren_directory(
&self,
request: DeleteWarrenDirectoryRequest,
) -> impl Future<Output = Result<FilePath, DeleteWarrenDirectoryError>> + Send;
fn upload_warren_files(
&self,
request: UploadWarrenFilesRequest,
) -> impl Future<Output = Result<Vec<FilePath>, UploadWarrenFilesError>> + Send;
fn delete_warren_file(
&self,
request: DeleteWarrenFileRequest,
) -> impl Future<Output = Result<FilePath, DeleteWarrenFileError>> + Send;
}
pub trait WarrenRepository: Clone + Send + Sync + 'static {
fn list_warrens(
&self,
request: ListWarrensRequest,
) -> impl Future<Output = Result<Vec<Warren>, ListWarrensError>> + Send;
fn fetch_warren(
&self,
request: FetchWarrenRequest,
) -> impl Future<Output = Result<Warren, FetchWarrenError>> + Send;
}
pub trait WarrenMetrics: Clone + Send + Sync + 'static {
fn record_warren_list_success(&self) -> impl Future<Output = ()> + Send;
fn record_warren_list_failure(&self) -> impl Future<Output = ()> + Send;
fn record_warren_fetch_success(&self) -> impl Future<Output = ()> + Send;
fn record_warren_fetch_failure(&self) -> impl Future<Output = ()> + Send;
fn record_list_warren_files_success(&self) -> impl Future<Output = ()> + Send;
fn record_list_warren_files_failure(&self) -> impl Future<Output = ()> + Send;
fn record_warren_directory_creation_success(&self) -> impl Future<Output = ()> + Send;
fn record_warren_directory_creation_failure(&self) -> impl Future<Output = ()> + Send;
fn record_warren_directory_deletion_success(&self) -> impl Future<Output = ()> + Send;
fn record_warren_directory_deletion_failure(&self) -> impl Future<Output = ()> + Send;
/// A single file upload succeeded
fn record_warren_file_upload_success(&self) -> impl Future<Output = ()> + Send;
/// A single file upload failed
fn record_warren_file_upload_failure(&self) -> impl Future<Output = ()> + Send;
/// An upload succeeded fully
fn record_warren_files_upload_success(&self) -> impl Future<Output = ()> + Send;
/// An upload failed at least partially
fn record_warren_files_upload_failure(&self) -> impl Future<Output = ()> + Send;
fn record_warren_file_deletion_success(&self) -> impl Future<Output = ()> + Send;
fn record_warren_file_deletion_failure(&self) -> impl Future<Output = ()> + Send;
}
pub trait WarrenNotifier: Clone + Send + Sync + 'static {
fn warrens_listed(&self, warrens: &Vec<Warren>) -> impl Future<Output = ()> + Send;
fn warren_fetched(&self, warren: &Warren) -> impl Future<Output = ()> + Send;
fn warren_files_listed(
&self,
warren: &Warren,
files: &Vec<File>,
) -> impl Future<Output = ()> + Send;
fn warren_directory_created(
&self,
warren: &Warren,
path: &FilePath,
) -> impl Future<Output = ()> + Send;
fn warren_directory_deleted(
&self,
warren: &Warren,
path: &FilePath,
) -> impl Future<Output = ()> + 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<Output = ()> + 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<Output = ()> + Send;
fn warren_file_deleted(
&self,
warren: &Warren,
path: &FilePath,
) -> impl Future<Output = ()> + Send;
}

View File

@@ -0,0 +1,47 @@
pub trait WarrenMetrics: Clone + Send + Sync + 'static {
fn record_warren_list_success(&self) -> impl Future<Output = ()> + Send;
fn record_warren_list_failure(&self) -> impl Future<Output = ()> + Send;
fn record_warren_fetch_success(&self) -> impl Future<Output = ()> + Send;
fn record_warren_fetch_failure(&self) -> impl Future<Output = ()> + Send;
fn record_list_warren_files_success(&self) -> impl Future<Output = ()> + Send;
fn record_list_warren_files_failure(&self) -> impl Future<Output = ()> + Send;
fn record_warren_directory_creation_success(&self) -> impl Future<Output = ()> + Send;
fn record_warren_directory_creation_failure(&self) -> impl Future<Output = ()> + Send;
fn record_warren_directory_deletion_success(&self) -> impl Future<Output = ()> + Send;
fn record_warren_directory_deletion_failure(&self) -> impl Future<Output = ()> + Send;
/// A single file upload succeeded
fn record_warren_file_upload_success(&self) -> impl Future<Output = ()> + Send;
/// A single file upload failed
fn record_warren_file_upload_failure(&self) -> impl Future<Output = ()> + Send;
/// An upload succeeded fully
fn record_warren_files_upload_success(&self) -> impl Future<Output = ()> + Send;
/// An upload failed at least partially
fn record_warren_files_upload_failure(&self) -> impl Future<Output = ()> + Send;
fn record_warren_file_deletion_success(&self) -> impl Future<Output = ()> + Send;
fn record_warren_file_deletion_failure(&self) -> impl Future<Output = ()> + Send;
}
pub trait FileSystemMetrics: Clone + Send + Sync + 'static {
fn record_list_files_success(&self) -> impl Future<Output = ()> + Send;
fn record_list_files_failure(&self) -> impl Future<Output = ()> + Send;
fn record_directory_creation_success(&self) -> impl Future<Output = ()> + Send;
fn record_directory_creation_failure(&self) -> impl Future<Output = ()> + Send;
fn record_directory_deletion_success(&self) -> impl Future<Output = ()> + Send;
fn record_directory_deletion_failure(&self) -> impl Future<Output = ()> + Send;
fn record_file_creation_success(&self) -> impl Future<Output = ()> + Send;
fn record_file_creation_failure(&self) -> impl Future<Output = ()> + Send;
fn record_file_deletion_success(&self) -> impl Future<Output = ()> + Send;
fn record_file_deletion_failure(&self) -> impl Future<Output = ()> + Send;
fn record_entry_rename_success(&self) -> impl Future<Output = ()> + Send;
fn record_entry_rename_failure(&self) -> impl Future<Output = ()> + Send;
}

View File

@@ -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<Output = Result<Vec<Warren>, ListWarrensError>> + Send;
fn fetch_warren(
&self,
request: FetchWarrenRequest,
) -> impl Future<Output = Result<Warren, FetchWarrenError>> + Send;
fn list_files(
&self,
request: ListWarrenFilesRequest,
) -> impl Future<Output = Result<Vec<File>, ListWarrenFilesError>> + Send;
fn create_warren_directory(
&self,
request: CreateWarrenDirectoryRequest,
) -> impl Future<Output = Result<FilePath, CreateWarrenDirectoryError>> + Send;
fn delete_warren_directory(
&self,
request: DeleteWarrenDirectoryRequest,
) -> impl Future<Output = Result<FilePath, DeleteWarrenDirectoryError>> + Send;
fn upload_warren_files(
&self,
request: UploadWarrenFilesRequest,
) -> impl Future<Output = Result<Vec<FilePath>, UploadWarrenFilesError>> + Send;
fn delete_warren_file(
&self,
request: DeleteWarrenFileRequest,
) -> impl Future<Output = Result<FilePath, DeleteWarrenFileError>> + Send;
}
pub trait FileSystemService: Clone + Send + Sync + 'static {
fn list_files(
&self,
request: ListFilesRequest,
) -> impl Future<Output = Result<Vec<File>, ListFilesError>> + Send;
fn create_directory(
&self,
request: CreateDirectoryRequest,
) -> impl Future<Output = Result<FilePath, CreateDirectoryError>> + Send;
fn delete_directory(
&self,
request: DeleteDirectoryRequest,
) -> impl Future<Output = Result<FilePath, DeleteDirectoryError>> + Send;
fn create_file(
&self,
request: CreateFileRequest,
) -> impl Future<Output = Result<FilePath, CreateFileError>> + Send;
fn delete_file(
&self,
request: DeleteFileRequest,
) -> impl Future<Output = Result<FilePath, DeleteFileError>> + Send;
fn rename_entry(
&self,
request: RenameEntryRequest,
) -> impl Future<Output = Result<FilePath, RenameEntryError>> + Send;
}

View File

@@ -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<Warren>) -> impl Future<Output = ()> + Send;
fn warren_fetched(&self, warren: &Warren) -> impl Future<Output = ()> + Send;
fn warren_files_listed(
&self,
warren: &Warren,
files: &Vec<File>,
) -> impl Future<Output = ()> + Send;
fn warren_directory_created(
&self,
warren: &Warren,
path: &FilePath,
) -> impl Future<Output = ()> + Send;
fn warren_directory_deleted(
&self,
warren: &Warren,
path: &FilePath,
) -> impl Future<Output = ()> + 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<Output = ()> + 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<Output = ()> + Send;
fn warren_file_deleted(
&self,
warren: &Warren,
path: &FilePath,
) -> impl Future<Output = ()> + Send;
}
pub trait FileSystemNotifier: Clone + Send + Sync + 'static {
fn files_listed(&self, files: &Vec<File>) -> impl Future<Output = ()> + Send;
fn directory_created(&self, path: &FilePath) -> impl Future<Output = ()> + Send;
fn directory_deleted(&self, path: &FilePath) -> impl Future<Output = ()> + Send;
fn file_created(&self, path: &FilePath) -> impl Future<Output = ()> + Send;
fn file_deleted(&self, path: &FilePath) -> impl Future<Output = ()> + Send;
fn entry_renamed(
&self,
old_path: &FilePath,
new_path: &FilePath,
) -> impl Future<Output = ()> + Send;
}

View File

@@ -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<Output = Result<Vec<Warren>, ListWarrensError>> + Send;
fn fetch_warren(
&self,
request: FetchWarrenRequest,
) -> impl Future<Output = Result<Warren, FetchWarrenError>> + Send;
}
pub trait FileSystemRepository: Clone + Send + Sync + 'static {
fn list_files(
&self,
request: ListFilesRequest,
) -> impl Future<Output = Result<Vec<File>, ListFilesError>> + Send;
fn create_directory(
&self,
request: CreateDirectoryRequest,
) -> impl Future<Output = Result<FilePath, CreateDirectoryError>> + Send;
fn delete_directory(
&self,
request: DeleteDirectoryRequest,
) -> impl Future<Output = Result<FilePath, DeleteDirectoryError>> + Send;
fn create_file(
&self,
request: CreateFileRequest,
) -> impl Future<Output = Result<FilePath, CreateFileError>> + Send;
fn delete_file(
&self,
request: DeleteFileRequest,
) -> impl Future<Output = Result<FilePath, DeleteFileError>> + Send;
fn rename_entry(
&self,
request: RenameEntryRequest,
) -> impl Future<Output = Result<FilePath, RenameEntryError>> + Send;
}

View File

@@ -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<Vec<super::models::file::File>, ListFilesError> {
async fn list_files(&self, request: ListFilesRequest) -> Result<Vec<File>, 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<FilePath, RenameEntryError> {
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
}
}

View File

@@ -0,0 +1,2 @@
pub mod file_system;
pub mod warren;

View File

@@ -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<Vec<Warren>, super::models::warren::ListWarrensError> {
request: ListWarrensRequest,
) -> Result<Vec<Warren>, ListWarrensError> {
let result = self.repository.list_warrens(request).await;
if let Ok(warren) = result.as_ref() {

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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<FilePath> {
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<FilePath, RenameEntryError> {
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)
}
}

View File

@@ -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");
}
}

View File

@@ -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);
}
}