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
118
119
120
use anyhow::Result;
use crate::{latest::LatestChapter, tag::ChapterTag, utils::permalink_from_title};
pub trait IntoDynastyChapterPath: IntoDynastyChapterPathSealed {}
impl IntoDynastyChapterPath for &ChapterTag {}
impl IntoDynastyChapterPath for ChapterTag {}
impl IntoDynastyChapterPath for &LatestChapter {}
impl IntoDynastyChapterPath for LatestChapter {}
impl IntoDynastyChapterPath for &str {}
impl IntoDynastyChapterPath for String {}
impl IntoDynastyChapterPath for &String {}
pub trait IntoDynastyChapterPathSealed {
fn dynasty_path(&self) -> Result<String>;
}
impl IntoDynastyChapterPathSealed for &ChapterTag {
fn dynasty_path(&self) -> Result<String> {
Ok(self.path())
}
}
impl IntoDynastyChapterPathSealed for ChapterTag {
fn dynasty_path(&self) -> Result<String> {
Ok(self.path())
}
}
impl IntoDynastyChapterPathSealed for &LatestChapter {
fn dynasty_path(&self) -> Result<String> {
Ok(self.path())
}
}
impl IntoDynastyChapterPathSealed for LatestChapter {
fn dynasty_path(&self) -> Result<String> {
Ok(self.path())
}
}
impl IntoDynastyChapterPathSealed for &str {
fn dynasty_path(&self) -> Result<String> {
let stripped = self.trim().trim_matches('/').to_lowercase();
let slash_count = stripped.matches('/').count();
anyhow::ensure!(!stripped.is_empty(), "chapter path must not be empty!");
let (prefix, s) = if slash_count == 1 {
anyhow::ensure!(
stripped.starts_with("chapters/"),
"chapter path must also starts with `chapters/` if it contains 1 `/`! {}",
self
);
self.split_once('/').unwrap()
} else if slash_count == 0 {
("chapters", stripped.as_str())
} else {
return Err(anyhow::anyhow!(
"chapter path must contains either 1 or 0 `/`! {}",
self
));
};
let permalink = permalink_from_title(s);
Ok(format!("{}/{}", prefix, permalink))
}
}
impl IntoDynastyChapterPathSealed for String {
fn dynasty_path(&self) -> Result<String> {
self.as_str().dynasty_path()
}
}
impl IntoDynastyChapterPathSealed for &String {
fn dynasty_path(&self) -> Result<String> {
self.as_str().dynasty_path()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn chapter_path_from_str() {
let sample = [
["kichiku_yousei", "chapters/kichiku_yousei"],
["chapters/a_channel_ch21", "chapters/a_channel_ch21"],
];
for [a, b] in sample {
assert_eq!(a.dynasty_path().unwrap(), b)
}
}
#[test]
fn chapter_path_from_invalid_str() {
let sample = [
["", "chapter path must not be empty!"],
[
"invalid/format/here",
"chapter path must contains either 1 or 0 `/`! invalid/format/here",
],
[
"this/is_invalid",
"chapter path must also starts with `chapters/` if it contains 1 `/`! this/is_invalid",
],
];
for [a, err] in sample {
assert_eq!(a.dynasty_path().unwrap_err().to_string(), err)
}
}
}