mirror of
https://github.com/Jackzmc/storage.git
synced 2025-05-06 21:33:20 +00:00
Implement folder sorting
This commit is contained in:
parent
6eb900b814
commit
d1cf5d3038
5 changed files with 49 additions and 7 deletions
|
@ -1,7 +1,9 @@
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use anyhow::Error;
|
use anyhow::{anyhow, Error};
|
||||||
|
use log::trace;
|
||||||
use rocket::response::stream::ReaderStream;
|
use rocket::response::stream::ReaderStream;
|
||||||
use rocket::serde::Serialize;
|
use rocket::serde::Serialize;
|
||||||
use tokio::io::BufStream;
|
use tokio::io::BufStream;
|
||||||
|
@ -17,6 +19,21 @@ pub struct Library {
|
||||||
repo: RepoContainer,
|
repo: RepoContainer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The direction of sort and the field to sort by
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub struct ListOptions {
|
||||||
|
pub sort_field: Option<String>,
|
||||||
|
pub sort_descending: Option<bool>
|
||||||
|
}
|
||||||
|
impl Default for ListOptions {
|
||||||
|
fn default() -> Self {
|
||||||
|
ListOptions {
|
||||||
|
sort_field: Some("name".to_string()),
|
||||||
|
sort_descending: Some(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Library {
|
impl Library {
|
||||||
pub fn new(library_model: LibraryModel, repo: RepoContainer) -> Library {
|
pub fn new(library_model: LibraryModel, repo: RepoContainer) -> Library {
|
||||||
Library {
|
Library {
|
||||||
|
@ -49,9 +66,28 @@ impl Library {
|
||||||
repo.backend.read_file(&self.model.id.to_string(), rel_path)
|
repo.backend.read_file(&self.model.id.to_string(), rel_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list_files(&self, rel_path: &PathBuf) -> Result<Vec<FileEntry>, anyhow::Error> {
|
pub async fn list_files(&self, rel_path: &PathBuf, options: ListOptions) -> Result<Vec<FileEntry>, anyhow::Error> {
|
||||||
let repo = self.repo.read().await;
|
let repo = self.repo.read().await;
|
||||||
repo.backend.list_files(&self.model.id.to_string(), rel_path)
|
let mut list = repo.backend.list_files(&self.model.id.to_string(), rel_path)?;
|
||||||
|
let field = options.sort_field.unwrap_or("name".to_string());
|
||||||
|
let descending = options.sort_descending.unwrap_or(false);
|
||||||
|
match field.as_str() {
|
||||||
|
"name" => list.sort_by(|a, b| {
|
||||||
|
if a._type == FileType::File && b._type != FileType::File { Ordering::Greater }
|
||||||
|
else if a._type != FileType::File && b._type == FileType::File { Ordering::Less }
|
||||||
|
else { a.path.cmp(&b.path) }
|
||||||
|
}),
|
||||||
|
"size" => list.sort_by(|a, b| {
|
||||||
|
if a._type == FileType::File && b._type != FileType::File { Ordering::Greater }
|
||||||
|
else if a._type != FileType::File && b._type == FileType::File { Ordering::Less }
|
||||||
|
else { a.size.cmp(&b.size) }
|
||||||
|
}),
|
||||||
|
_ => return Err(anyhow!("Unsupported field"))
|
||||||
|
}
|
||||||
|
if descending {
|
||||||
|
list.reverse();
|
||||||
|
}
|
||||||
|
Ok(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_file(&self, rel_path: &PathBuf) -> Result<(), anyhow::Error> {
|
pub async fn delete_file(&self, rel_path: &PathBuf) -> Result<(), anyhow::Error> {
|
||||||
|
|
|
@ -17,6 +17,7 @@ use crate::managers::libraries::LibraryManager;
|
||||||
use crate::managers::repos::RepoManager;
|
use crate::managers::repos::RepoManager;
|
||||||
use crate::models::library::{LibraryModel, LibraryWithRepoModel};
|
use crate::models::library::{LibraryModel, LibraryWithRepoModel};
|
||||||
use crate::models::user;
|
use crate::models::user;
|
||||||
|
use crate::objs::library::ListOptions;
|
||||||
use crate::storage::{FileEntry, FileType};
|
use crate::storage::{FileEntry, FileType};
|
||||||
use crate::util::{JsonErrorResponse, ResponseError};
|
use crate::util::{JsonErrorResponse, ResponseError};
|
||||||
#[get("/<library_id>")]
|
#[get("/<library_id>")]
|
||||||
|
@ -30,7 +31,7 @@ pub(crate) async fn get_file(pool: &State<DB>, library_id: &str) -> Result<Optio
|
||||||
pub(crate) async fn list_files(libraries: &State<Arc<Mutex<LibraryManager>>>, library_id: &str, path: &str) -> Result<Json<Vec<FileEntry>>, ResponseError> {
|
pub(crate) async fn list_files(libraries: &State<Arc<Mutex<LibraryManager>>>, library_id: &str, path: &str) -> Result<Json<Vec<FileEntry>>, ResponseError> {
|
||||||
let libs = libraries.lock().await;
|
let libs = libraries.lock().await;
|
||||||
let library = libs.get(library_id).await?;
|
let library = libs.get(library_id).await?;
|
||||||
library.list_files(&PathBuf::from(path)).await
|
library.list_files(&PathBuf::from(path), ListOptions::default()).await
|
||||||
.map(|files| Json(files))
|
.map(|files| Json(files))
|
||||||
.map_err(|e| ResponseError::InternalServerError(JsonErrorResponse {
|
.map_err(|e| ResponseError::InternalServerError(JsonErrorResponse {
|
||||||
code: "STORAGE_ERROR".to_string(),
|
code: "STORAGE_ERROR".to_string(),
|
||||||
|
|
|
@ -17,6 +17,7 @@ use tokio::sync::Mutex;
|
||||||
use crate::consts::FILE_CONSTANTS;
|
use crate::consts::FILE_CONSTANTS;
|
||||||
use crate::guards::{AuthUser};
|
use crate::guards::{AuthUser};
|
||||||
use crate::managers::libraries::LibraryManager;
|
use crate::managers::libraries::LibraryManager;
|
||||||
|
use crate::objs::library::ListOptions;
|
||||||
use crate::routes::ui::auth;
|
use crate::routes::ui::auth;
|
||||||
use crate::util::{JsonErrorResponse, ResponseError};
|
use crate::util::{JsonErrorResponse, ResponseError};
|
||||||
|
|
||||||
|
@ -55,7 +56,11 @@ pub async fn list_library_files(
|
||||||
};
|
};
|
||||||
let libs = libraries.lock().await;
|
let libs = libraries.lock().await;
|
||||||
let library = libs.get(library_id).await?;
|
let library = libs.get(library_id).await?;
|
||||||
let files = library.list_files(&PathBuf::from(&path)).await
|
let list_options = ListOptions {
|
||||||
|
sort_field: Some(options.sort_key.clone()),
|
||||||
|
sort_descending: Some(options.sort_dir == "desc"),
|
||||||
|
};
|
||||||
|
let files = library.list_files(&PathBuf::from(&path), list_options).await
|
||||||
.map_err(|e| ResponseError::InternalServerError(JsonErrorResponse {
|
.map_err(|e| ResponseError::InternalServerError(JsonErrorResponse {
|
||||||
code: "STORAGE_ERROR".to_string(),
|
code: "STORAGE_ERROR".to_string(),
|
||||||
message: e.to_string(),
|
message: e.to_string(),
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub enum StorageBackendMap {
|
||||||
S3(S3Storage)
|
S3(S3Storage)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, FromFormField)]
|
#[derive(Debug, Serialize, Deserialize, FromFormField, PartialEq)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum FileType {
|
pub enum FileType {
|
||||||
File,
|
File,
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Username / Email</label>
|
<label class="label">Username / Email</label>
|
||||||
<div class="control has-icons-left">
|
<div class="control has-icons-left">
|
||||||
<input required name="username" class="input {{#if errors.username}}is-danger{{/if}}" type="text" placeholder="Username or Email">
|
<input autofocus required name="username" class="input {{#if errors.username}}is-danger{{/if}}" type="text" placeholder="Username or Email">
|
||||||
<span class="icon is-small is-left">
|
<span class="icon is-small is-left">
|
||||||
<i class="fas fa-user"></i>
|
<i class="fas fa-user"></i>
|
||||||
</span>
|
</span>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue