Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
964 changes: 483 additions & 481 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ hightorrent_api = { git = "https://github.com/angrynode/hightorrent_api" }
# hightorrent_api = "0.2"
log = "0.4.27"
# SQLite ORM
sea-orm = { version = "2.0.0-rc.18", features = [ "runtime-tokio", "debug-print", "sqlx-sqlite"] }
sea-orm = { version = "2.0.0-rc.21", features = [ "runtime-tokio", "debug-print", "sqlx-sqlite"] }
# SQLite migrations
sea-orm-migration = { version = "2.0.0-rc.18" }
sea-orm-migration = { version = "2.0.0-rc.21" }
# Serialization/deserialization, for example in path extractors
serde = { version = "1.0.219", features = ["derive", "rc"] }
# (De)serialization for operations log
Expand Down
13 changes: 7 additions & 6 deletions src/database/category.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use sea_orm::*;
use snafu::prelude::*;

use crate::database::operation::*;
use crate::extractors::normalized_path::*;
use crate::extractors::user::User;
use crate::routes::category::CategoryForm;
use crate::state::AppState;
Expand All @@ -20,9 +21,9 @@ pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(unique)]
pub name: String,
pub name: NormalizedPathComponent,
#[sea_orm(unique)]
pub path: String,
pub path: NormalizedPathAbsolute,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
Expand Down Expand Up @@ -88,7 +89,7 @@ impl CategoryOperator {
operation: OperationType::Delete,
operation_id: OperationId {
object_id: category_clone.id,
name: category_clone.name.to_owned(),
name: category_clone.name.to_string(),
},
operation_form: None,
};
Expand All @@ -99,7 +100,7 @@ impl CategoryOperator {
.await
.context(LoggerSnafu)?;

Ok(category_clone.name)
Ok(category_clone.name.to_string())
}
None => Err(CategoryError::NotFound { id }),
}
Expand Down Expand Up @@ -130,12 +131,12 @@ impl CategoryOperator {

if list.iter().any(|x| x.name == f.name) {
return Err(CategoryError::NameTaken {
name: f.name.clone(),
name: f.name.to_string(),
});
}
if list.iter().any(|x| x.path == f.path) {
return Err(CategoryError::PathTaken {
path: f.path.clone(),
path: f.path.to_string(),
});
}

Expand Down
1 change: 1 addition & 0 deletions src/extractors/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod normalized_path;
pub mod torrent_list;
pub mod user;
93 changes: 93 additions & 0 deletions src/extractors/normalized_path/absolute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use camino::Utf8PathBuf;
use sea_orm::*;
use serde::{Deserialize, Serialize};

use std::ops::Deref;
use std::path::Path;
use std::str::FromStr;

use super::*;

/// [NormalizedPath] with extra constraint that it's absolute.
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Hash, DeriveValueType)]
#[sea_orm(value_type = "String")]
#[serde(into = "String", try_from = "String")]
pub struct NormalizedPathAbsolute {
path: Utf8PathBuf,
}

impl NormalizedPathAbsolute {
pub fn to_path_buf(&self) -> Utf8PathBuf {
self.path.clone()
}

pub fn as_str(&self) -> &str {
self.as_ref()
}
}

impl std::fmt::Display for NormalizedPathAbsolute {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.path)
}
}

impl FromStr for NormalizedPathAbsolute {
type Err = NormalizeError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let p = NormalizedPath::from_str(s)?;
if !p.as_str().starts_with("/") {
return Err(NormalizeError::new(
p.to_path_buf(),
NormalizeErrorKind::AbsoluteRequired,
));
}

Ok(Self {
path: p.to_path_buf(),
})
}
}

impl TryFrom<String> for NormalizedPathAbsolute {
type Error = NormalizeError;

fn try_from(s: String) -> Result<Self, Self::Error> {
Self::from_str(&s)
}
}

impl TryFrom<Utf8PathBuf> for NormalizedPathAbsolute {
type Error = NormalizeError;

fn try_from(p: Utf8PathBuf) -> Result<Self, Self::Error> {
Self::from_str(p.as_ref())
}
}

impl From<NormalizedPathAbsolute> for String {
fn from(p: NormalizedPathAbsolute) -> Self {
p.to_string()
}
}

impl Deref for NormalizedPathAbsolute {
type Target = Utf8PathBuf;

fn deref(&self) -> &Self::Target {
&self.path
}
}

impl AsRef<str> for NormalizedPathAbsolute {
fn as_ref(&self) -> &str {
self.path.as_str()
}
}

impl AsRef<Path> for NormalizedPathAbsolute {
fn as_ref(&self) -> &Path {
self.path.as_std_path()
}
}
100 changes: 100 additions & 0 deletions src/extractors/normalized_path/component.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use camino::Utf8PathBuf;
use sea_orm::*;
use serde::{Deserialize, Serialize};

use std::ops::Deref;
use std::path::Path;
use std::str::FromStr;

use super::*;

/// [NormalizedPath] with extra constraint that it contains no slashes.
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Hash, DeriveValueType)]
#[sea_orm(value_type = "String")]
#[serde(into = "String", try_from = "String")]
pub struct NormalizedPathComponent {
path: Utf8PathBuf,
}

impl NormalizedPathComponent {
pub fn to_path_buf(&self) -> Utf8PathBuf {
self.path.clone()
}

pub fn as_str(&self) -> &str {
self.as_ref()
}
}

impl std::fmt::Display for NormalizedPathComponent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.path)
}
}

impl FromStr for NormalizedPathComponent {
type Err = NormalizeError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.contains("/") {
return Err(NormalizeError::new(
Utf8PathBuf::from(s),
NormalizeErrorKind::SlashForbidden,
));
}

if s == "." {
return Err(NormalizeError::new(
Utf8PathBuf::from(s),
NormalizeErrorKind::CurrentDirNotAllowed,
));
}

let p = NormalizedPath::from_str(s)?;
Ok(Self {
path: p.to_path_buf(),
})
}
}

impl TryFrom<String> for NormalizedPathComponent {
type Error = NormalizeError;

fn try_from(s: String) -> Result<Self, Self::Error> {
Self::from_str(&s)
}
}

impl TryFrom<Utf8PathBuf> for NormalizedPathComponent {
type Error = NormalizeError;

fn try_from(p: Utf8PathBuf) -> Result<Self, Self::Error> {
Self::from_str(p.as_ref())
}
}

impl From<NormalizedPathComponent> for String {
fn from(p: NormalizedPathComponent) -> Self {
p.to_string()
}
}

impl Deref for NormalizedPathComponent {
type Target = Utf8PathBuf;

fn deref(&self) -> &Self::Target {
&self.path
}
}

impl AsRef<str> for NormalizedPathComponent {
fn as_ref(&self) -> &str {
self.path.as_str()
}
}

impl AsRef<Path> for NormalizedPathComponent {
fn as_ref(&self) -> &Path {
self.path.as_std_path()
}
}
38 changes: 38 additions & 0 deletions src/extractors/normalized_path/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use camino::Utf8PathBuf;
use snafu::prelude::*;

#[derive(Clone, Debug, PartialEq)]
pub struct NormalizeError {
pub path: Utf8PathBuf,
pub kind: NormalizeErrorKind,
}

impl NormalizeError {
pub fn new(path: Utf8PathBuf, kind: NormalizeErrorKind) -> Self {
Self { path, kind }
}
}

impl std::error::Error for NormalizeError {}

#[derive(Clone, Debug, PartialEq, Snafu)]
pub enum NormalizeErrorKind {
#[snafu(display("path cannot be empty"))]
EmptyNotAllowed,
#[snafu(display("`..` not allowed in path"))]
ParentNotAllowed,
#[snafu(display("`.` not allowed in path"))]
CurrentDirNotAllowed,
#[snafu(display("Path must relative"))]
AbsoluteNotAllowed,
#[snafu(display("Path must absolute"))]
AbsoluteRequired,
#[snafu(display("Component cannot contain slash"))]
SlashForbidden,
}

impl std::fmt::Display for NormalizeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}{}", self.path, self.kind)
}
}
Loading