-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathoperator.rs
More file actions
117 lines (99 loc) · 3.24 KB
/
Copy pathoperator.rs
File metadata and controls
117 lines (99 loc) · 3.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use sea_orm::*;
use snafu::prelude::*;
use std::ops::Deref;
use std::str::FromStr;
use crate::database::operation::Table;
use crate::database::operator::{DatabaseOperator, TableOperator};
use crate::extractors::normalized_path::NormalizedPathComponent;
use super::*;
#[derive(Clone, Debug)]
pub struct ContentFolderOperator<'a> {
pub db: &'a DatabaseOperator,
}
impl Deref for ContentFolderOperator<'_> {
type Target = DatabaseOperator;
fn deref(&self) -> &DatabaseOperator {
self.db
}
}
impl TableOperator for ContentFolderOperator<'_> {
fn table(&self) -> Table {
Table::ContentFolder
}
}
impl ContentFolderOperator<'_> {
/// List content folders
///
/// Should not fail, unless SQLite was corrupted for some reason.
pub async fn list(&self) -> Result<Vec<Model>, ContentFolderError> {
Entity::find()
.all(&self.state.database)
.await
.context(DBSnafu)
}
/// Find one content folder by ID
///
/// Fails if:
///
/// - the requested ID does not exist
pub async fn find_by_id(&self, id: i32) -> Result<Model, ContentFolderError> {
let content_folder = Entity::find_by_id(id)
.one(&self.state.database)
.await
.context(DBSnafu)?;
match content_folder {
Some(category) => Ok(category),
None => Err(ContentFolderError::NotFound { id }),
}
}
/// Create a new content folder
///
/// Fails if:
///
/// - name is already taken (they should be unique in one folder)
/// - path parent directory does not exist (to avoid completely wrong paths)
pub async fn create(
&self,
parent: Option<Model>,
name: String,
) -> Result<Model, ContentFolderError> {
let name = NormalizedPathComponent::from_str(&name)
.map_err(|_e| ContentFolderError::NameInvalid)?;
let list = self.list().await?;
let siblings = parent
.as_ref()
.map(|x| x.children_from_list(&list))
.unwrap_or(vec![]);
if siblings.iter().any(|x| x.name == name) {
return Err(ContentFolderError::NameTaken {
name: name.to_string(),
});
}
let model = ActiveModel {
name: Set(name),
parent_id: Set(parent.as_ref().map(|x| x.id)),
..Default::default()
}
.save(&self.state.database)
.await
.context(DBSnafu)?;
// Should not fail
let model = model.try_into_model().unwrap();
// If the folder already exists, it's not an error. Maybe it was
// created manually before importing to TorrentManager.
let real_path = model.path_from_list(&self.state.config.media_dir, &list);
if !tokio::fs::try_exists(&real_path).await.context(IOSnafu)? {
tokio::fs::create_dir_all(&real_path)
.await
.context(IOSnafu)?;
}
self.log_create(ContentFolderOperation::Create {
id: model.id,
name: model.name.to_string(),
parent: parent.as_ref().map(|x| (x.id, x.name.to_string())),
})
.await
.context(LoggerSnafu)?;
Ok(model)
}
}