directory back up (parent) button + drag entry into parent to move
This commit is contained in:
@@ -1,21 +1,29 @@
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::domain::warren::models::file::AbsoluteFilePath;
|
||||
use crate::domain::warren::models::file::{AbsoluteFilePath, File};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct LsRequest {
|
||||
path: AbsoluteFilePath,
|
||||
include_parent: bool,
|
||||
}
|
||||
|
||||
impl LsRequest {
|
||||
pub fn new(path: AbsoluteFilePath) -> Self {
|
||||
Self { path }
|
||||
pub fn new(path: AbsoluteFilePath, include_parent: bool) -> Self {
|
||||
Self {
|
||||
path,
|
||||
include_parent,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &AbsoluteFilePath {
|
||||
&self.path
|
||||
}
|
||||
|
||||
pub fn include_parent(&self) -> bool {
|
||||
self.include_parent
|
||||
}
|
||||
|
||||
pub fn into_path(self) -> AbsoluteFilePath {
|
||||
self.path
|
||||
}
|
||||
@@ -28,3 +36,23 @@ pub enum LsError {
|
||||
#[error(transparent)]
|
||||
Unknown(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct LsResponse {
|
||||
files: Vec<File>,
|
||||
parent: Option<File>,
|
||||
}
|
||||
|
||||
impl LsResponse {
|
||||
pub fn new(files: Vec<File>, parent: Option<File>) -> Self {
|
||||
Self { files, parent }
|
||||
}
|
||||
|
||||
pub fn files(&self) -> &Vec<File> {
|
||||
&self.files
|
||||
}
|
||||
|
||||
pub fn parent(&self) -> Option<&File> {
|
||||
self.parent.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ use futures_util::StreamExt;
|
||||
use thiserror::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::domain::warren::models::file::LsResponse;
|
||||
use crate::domain::warren::models::file::SaveResponse;
|
||||
use crate::domain::warren::models::file::{
|
||||
AbsoluteFilePath, CatError, CatRequest, File, FileName, LsError, LsRequest, MkdirError,
|
||||
MkdirRequest, MvError, MvRequest, RmError, RmRequest, SaveError, SaveRequest, TouchError,
|
||||
TouchRequest,
|
||||
AbsoluteFilePath, CatError, CatRequest, FileName, LsError, LsRequest, MkdirError, MkdirRequest,
|
||||
MvError, MvRequest, RmError, RmRequest, SaveError, SaveRequest, TouchError, TouchRequest,
|
||||
};
|
||||
|
||||
use super::{Warren, WarrenName};
|
||||
@@ -77,12 +77,13 @@ impl WarrenLsRequest {
|
||||
}
|
||||
|
||||
pub fn build_fs_request(self, warren: &Warren) -> LsRequest {
|
||||
let include_parent = self.base.include_parent();
|
||||
let path = warren
|
||||
.path()
|
||||
.clone()
|
||||
.join(&self.base.into_path().to_relative());
|
||||
|
||||
LsRequest::new(path)
|
||||
LsRequest::new(path, include_parent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,16 +97,12 @@ impl Into<FetchWarrenRequest> for WarrenLsRequest {
|
||||
pub struct WarrenLsResponse {
|
||||
warren: Warren,
|
||||
path: AbsoluteFilePath,
|
||||
files: Vec<File>,
|
||||
base: LsResponse,
|
||||
}
|
||||
|
||||
impl WarrenLsResponse {
|
||||
pub fn new(warren: Warren, path: AbsoluteFilePath, files: Vec<File>) -> Self {
|
||||
Self {
|
||||
warren,
|
||||
path,
|
||||
files,
|
||||
}
|
||||
pub fn new(warren: Warren, path: AbsoluteFilePath, base: LsResponse) -> Self {
|
||||
Self { warren, path, base }
|
||||
}
|
||||
|
||||
pub fn warren(&self) -> &Warren {
|
||||
@@ -116,8 +113,8 @@ impl WarrenLsResponse {
|
||||
&self.path
|
||||
}
|
||||
|
||||
pub fn files(&self) -> &Vec<File> {
|
||||
&self.files
|
||||
pub fn base(&self) -> &LsResponse {
|
||||
&self.base
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ use super::models::{
|
||||
},
|
||||
},
|
||||
file::{
|
||||
CatError, CatRequest, File, FileStream, LsError, LsRequest, MkdirError, MkdirRequest,
|
||||
CatError, CatRequest, FileStream, LsError, LsRequest, LsResponse, MkdirError, MkdirRequest,
|
||||
MvError, MvRequest, RmError, RmRequest, SaveError, SaveRequest, SaveResponse, TouchError,
|
||||
TouchRequest,
|
||||
},
|
||||
@@ -103,7 +103,7 @@ pub trait WarrenService: Clone + Send + Sync + 'static {
|
||||
}
|
||||
|
||||
pub trait FileSystemService: Clone + Send + Sync + 'static {
|
||||
fn ls(&self, request: LsRequest) -> impl Future<Output = Result<Vec<File>, LsError>> + Send;
|
||||
fn ls(&self, request: LsRequest) -> impl Future<Output = Result<LsResponse, LsError>> + Send;
|
||||
fn cat(&self, request: CatRequest)
|
||||
-> impl Future<Output = Result<FileStream, CatError>> + Send;
|
||||
fn mkdir(&self, request: MkdirRequest) -> impl Future<Output = Result<(), MkdirError>> + Send;
|
||||
|
||||
@@ -2,7 +2,7 @@ use uuid::Uuid;
|
||||
|
||||
use crate::domain::warren::models::{
|
||||
auth_session::requests::FetchAuthSessionResponse,
|
||||
file::{AbsoluteFilePath, File},
|
||||
file::{AbsoluteFilePath, LsResponse},
|
||||
user::{ListAllUsersAndWarrensResponse, LoginUserResponse, User},
|
||||
user_warren::UserWarren,
|
||||
warren::{
|
||||
@@ -46,7 +46,7 @@ pub trait WarrenNotifier: Clone + Send + Sync + 'static {
|
||||
}
|
||||
|
||||
pub trait FileSystemNotifier: Clone + Send + Sync + 'static {
|
||||
fn ls(&self, files: &Vec<File>) -> impl Future<Output = ()> + Send;
|
||||
fn ls(&self, response: &LsResponse) -> impl Future<Output = ()> + Send;
|
||||
fn cat(&self, path: &AbsoluteFilePath) -> impl Future<Output = ()> + Send;
|
||||
fn mkdir(&self, path: &AbsoluteFilePath) -> impl Future<Output = ()> + Send;
|
||||
fn rm(&self, path: &AbsoluteFilePath) -> impl Future<Output = ()> + Send;
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::domain::warren::models::{
|
||||
},
|
||||
},
|
||||
file::{
|
||||
CatError, CatRequest, File, FileStream, LsError, LsRequest, MkdirError, MkdirRequest,
|
||||
CatError, CatRequest, FileStream, LsError, LsRequest, LsResponse, MkdirError, MkdirRequest,
|
||||
MvError, MvRequest, RmError, RmRequest, SaveError, SaveRequest, SaveResponse, TouchError,
|
||||
TouchRequest,
|
||||
},
|
||||
@@ -66,7 +66,7 @@ pub trait WarrenRepository: Clone + Send + Sync + 'static {
|
||||
}
|
||||
|
||||
pub trait FileSystemRepository: Clone + Send + Sync + 'static {
|
||||
fn ls(&self, request: LsRequest) -> impl Future<Output = Result<Vec<File>, LsError>> + Send;
|
||||
fn ls(&self, request: LsRequest) -> impl Future<Output = Result<LsResponse, LsError>> + Send;
|
||||
fn cat(&self, request: CatRequest)
|
||||
-> impl Future<Output = Result<FileStream, CatError>> + Send;
|
||||
fn mkdir(&self, request: MkdirRequest) -> impl Future<Output = Result<(), MkdirError>> + Send;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::domain::warren::{
|
||||
models::file::{
|
||||
CatError, CatRequest, File, FileStream, LsError, LsRequest, MkdirError, MkdirRequest,
|
||||
CatError, CatRequest, FileStream, LsError, LsRequest, LsResponse, MkdirError, MkdirRequest,
|
||||
MvError, MvRequest, RmError, RmRequest, SaveError, SaveRequest, SaveResponse, TouchError,
|
||||
TouchRequest,
|
||||
},
|
||||
@@ -40,7 +40,7 @@ where
|
||||
M: FileSystemMetrics,
|
||||
N: FileSystemNotifier,
|
||||
{
|
||||
async fn ls(&self, request: LsRequest) -> Result<Vec<File>, LsError> {
|
||||
async fn ls(&self, request: LsRequest) -> Result<LsResponse, LsError> {
|
||||
let result = self.repository.ls(request).await;
|
||||
|
||||
if let Ok(files) = result.as_ref() {
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
AbsoluteFilePathError, File, FileMimeType, FilePath, FilePathError, FileType,
|
||||
LsRequest,
|
||||
},
|
||||
warren::WarrenLsRequest,
|
||||
warren::{WarrenLsRequest, WarrenLsResponse},
|
||||
},
|
||||
ports::{AuthService, WarrenService},
|
||||
},
|
||||
@@ -41,7 +41,10 @@ impl WarrenLsHttpRequestBody {
|
||||
fn try_into_domain(self) -> Result<WarrenLsRequest, ParseWarrenLsHttpRequestError> {
|
||||
let path = FilePath::new(&self.path)?.try_into()?;
|
||||
|
||||
Ok(WarrenLsRequest::new(self.warren_id, LsRequest::new(path)))
|
||||
Ok(WarrenLsRequest::new(
|
||||
self.warren_id,
|
||||
LsRequest::new(path, &self.path != "/"),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +78,7 @@ pub struct WarrenFileElement {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ListWarrenFilesResponseData {
|
||||
files: Vec<WarrenFileElement>,
|
||||
parent: Option<WarrenFileElement>,
|
||||
}
|
||||
|
||||
impl From<&File> for WarrenFileElement {
|
||||
@@ -88,10 +92,16 @@ impl From<&File> for WarrenFileElement {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Vec<File>> for ListWarrenFilesResponseData {
|
||||
fn from(value: &Vec<File>) -> Self {
|
||||
impl From<WarrenLsResponse> for ListWarrenFilesResponseData {
|
||||
fn from(value: WarrenLsResponse) -> Self {
|
||||
Self {
|
||||
files: value.iter().map(WarrenFileElement::from).collect(),
|
||||
files: value
|
||||
.base()
|
||||
.files()
|
||||
.iter()
|
||||
.map(WarrenFileElement::from)
|
||||
.collect(),
|
||||
parent: value.base().parent().map(WarrenFileElement::from),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,6 +117,6 @@ pub async fn list_warren_files<WS: WarrenService, AS: AuthService>(
|
||||
.auth_service
|
||||
.auth_warren_ls(domain_request, state.warren_service.as_ref())
|
||||
.await
|
||||
.map(|response| ApiSuccess::new(StatusCode::OK, response.files().into()))
|
||||
.map(|response| ApiSuccess::new(StatusCode::OK, response.into()))
|
||||
.map_err(ApiError::from)
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ use crate::{
|
||||
models::{
|
||||
file::{
|
||||
AbsoluteFilePath, CatError, CatRequest, File, FileMimeType, FileName, FilePath,
|
||||
FileStream, FileType, LsError, LsRequest, MkdirError, MkdirRequest, MvError,
|
||||
MvRequest, RelativeFilePath, RmError, RmRequest, SaveError, SaveRequest,
|
||||
FileStream, FileType, LsError, LsRequest, LsResponse, MkdirError, MkdirRequest,
|
||||
MvError, MvRequest, RelativeFilePath, RmError, RmRequest, SaveError, SaveRequest,
|
||||
SaveResponse, TouchError, TouchRequest,
|
||||
},
|
||||
warren::UploadFileStream,
|
||||
@@ -75,13 +75,38 @@ impl FileSystem {
|
||||
self.base_directory.join(&path.as_relative())
|
||||
}
|
||||
|
||||
async fn get_all_files(&self, absolute_path: &AbsoluteFilePath) -> anyhow::Result<Vec<File>> {
|
||||
let directory_path = self.get_target_path(absolute_path);
|
||||
|
||||
let mut dir = fs::read_dir(&directory_path).await?;
|
||||
async fn get_all_files(
|
||||
&self,
|
||||
absolute_path: &AbsoluteFilePath,
|
||||
include_parent: bool,
|
||||
) -> anyhow::Result<LsResponse> {
|
||||
let dir_path = self.get_target_path(absolute_path).as_ref().to_path_buf();
|
||||
|
||||
let mut files = Vec::new();
|
||||
|
||||
let parent = if include_parent {
|
||||
let dir_name = FileName::new(
|
||||
&dir_path
|
||||
.file_name()
|
||||
.context("Failed to get directory name")?
|
||||
.to_owned()
|
||||
.into_string()
|
||||
.ok()
|
||||
.context("Failed to get directory name")?,
|
||||
)?;
|
||||
|
||||
Some(File::new(
|
||||
dir_name,
|
||||
FileType::Directory,
|
||||
None,
|
||||
get_btime(&dir_path),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut dir = fs::read_dir(&dir_path).await?;
|
||||
|
||||
while let Ok(Some(entry)) = dir.next_entry().await {
|
||||
let name = entry
|
||||
.file_name()
|
||||
@@ -100,18 +125,7 @@ impl FileSystem {
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Use `DirEntry::metadata` once `target=x86_64-unknown-linux-musl` updates from musl 1.2.3 to 1.2.5
|
||||
// https://github.com/rust-lang/rust/pull/142682
|
||||
let created_at = unsafe {
|
||||
statx(
|
||||
std::os::fd::BorrowedFd::borrow_raw(-100),
|
||||
entry.path(),
|
||||
rustix::fs::AtFlags::empty(),
|
||||
rustix::fs::StatxFlags::BTIME,
|
||||
)
|
||||
}
|
||||
.ok()
|
||||
.map(|statx| statx.stx_btime.tv_sec as u64);
|
||||
let created_at = get_btime(entry.path());
|
||||
|
||||
let mime_type = match file_type {
|
||||
FileType::File => FileMimeType::from_name(&name),
|
||||
@@ -126,7 +140,7 @@ impl FileSystem {
|
||||
));
|
||||
}
|
||||
|
||||
Ok(files)
|
||||
Ok(LsResponse::new(files, parent))
|
||||
}
|
||||
|
||||
/// Actually created a directory in the underlying file system
|
||||
@@ -233,13 +247,16 @@ impl FileSystem {
|
||||
}
|
||||
|
||||
impl FileSystemRepository for FileSystem {
|
||||
async fn ls(&self, request: LsRequest) -> Result<Vec<File>, LsError> {
|
||||
let files = self.get_all_files(request.path()).await.map_err(|err| {
|
||||
anyhow!(err).context(format!(
|
||||
"Failed to get the files at path: {}",
|
||||
request.path()
|
||||
))
|
||||
})?;
|
||||
async fn ls(&self, request: LsRequest) -> Result<LsResponse, LsError> {
|
||||
let files = self
|
||||
.get_all_files(request.path(), request.include_parent())
|
||||
.await
|
||||
.map_err(|err| {
|
||||
anyhow!(err).context(format!(
|
||||
"Failed to get the files at path: {}",
|
||||
request.path()
|
||||
))
|
||||
})?;
|
||||
|
||||
Ok(files)
|
||||
}
|
||||
@@ -297,3 +314,21 @@ impl FileSystemRepository for FileSystem {
|
||||
Ok(self.save(&path, &mut stream).await.map(SaveResponse::new)?)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Use `DirEntry::metadata` once `target=x86_64-unknown-linux-musl` updates from musl 1.2.3 to 1.2.5
|
||||
// https://github.com/rust-lang/rust/pull/142682
|
||||
fn get_btime<P>(path: P) -> Option<u64>
|
||||
where
|
||||
P: rustix::path::Arg,
|
||||
{
|
||||
unsafe {
|
||||
statx(
|
||||
std::os::fd::BorrowedFd::borrow_raw(-100),
|
||||
path,
|
||||
rustix::fs::AtFlags::empty(),
|
||||
rustix::fs::StatxFlags::BTIME,
|
||||
)
|
||||
}
|
||||
.ok()
|
||||
.map(|statx| statx.stx_btime.tv_sec as u64)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use uuid::Uuid;
|
||||
use crate::domain::warren::{
|
||||
models::{
|
||||
auth_session::requests::FetchAuthSessionResponse,
|
||||
file::{AbsoluteFilePath, File},
|
||||
file::{AbsoluteFilePath, LsResponse},
|
||||
user::{ListAllUsersAndWarrensResponse, LoginUserResponse, User},
|
||||
user_warren::UserWarren,
|
||||
warren::{
|
||||
@@ -57,7 +57,7 @@ impl WarrenNotifier for NotifierDebugLogger {
|
||||
async fn warren_ls(&self, response: &WarrenLsResponse) {
|
||||
tracing::debug!(
|
||||
"[Notifier] Listed {} file(s) in warren {}",
|
||||
response.files().len(),
|
||||
response.base().files().len(),
|
||||
response.warren().name()
|
||||
);
|
||||
}
|
||||
@@ -101,8 +101,8 @@ impl WarrenNotifier for NotifierDebugLogger {
|
||||
}
|
||||
|
||||
impl FileSystemNotifier for NotifierDebugLogger {
|
||||
async fn ls(&self, files: &Vec<File>) {
|
||||
tracing::debug!("[Notifier] Listed {} file(s)", files.len());
|
||||
async fn ls(&self, response: &LsResponse) {
|
||||
tracing::debug!("[Notifier] Listed {} file(s)", response.files().len());
|
||||
}
|
||||
|
||||
async fn cat(&self, path: &AbsoluteFilePath) {
|
||||
@@ -279,7 +279,7 @@ impl AuthNotifier for NotifierDebugLogger {
|
||||
async fn auth_warren_ls(&self, user: &User, response: &WarrenLsResponse) {
|
||||
tracing::debug!(
|
||||
"[Notifier] Listed {} file(s) in warren {} for authenticated user {}",
|
||||
response.files().len(),
|
||||
response.base().files().len(),
|
||||
response.warren().name(),
|
||||
user.id()
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user