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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#![crate_name = "tanoshi_lib"]
pub static CORE_VERSION: &str = env!("CARGO_PKG_VERSION");
pub static RUSTC_VERSION: &str = env!("RUSTC_VERSION");
#[cfg(feature = "model")]
pub mod model {
use core::f64;
#[derive(Debug, Clone, Default)]
pub struct SourceLogin {
pub username: String,
pub password: String,
pub remember_me: Option<bool>,
pub two_factor: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub struct SourceLoginResult {
pub source_name: String,
pub auth_type: String,
pub value: String,
}
#[derive(Debug, Clone)]
pub struct Source {
pub id: i64,
pub name: String,
pub url: String,
pub version: String,
pub icon: String,
pub need_login: bool,
}
#[derive(Debug, Clone, Default)]
pub struct Manga {
pub source_id: i64,
pub title: String,
pub author: Vec<String>,
pub genre: Vec<String>,
pub status: Option<String>,
pub description: Option<String>,
pub path: String,
pub cover_url: String,
}
#[derive(Debug, Clone)]
pub struct Chapter {
pub source_id: i64,
pub title: String,
pub path: String,
pub number: f64,
pub scanlator: String,
pub uploaded: chrono::NaiveDateTime,
}
#[derive(Debug, Clone, PartialEq)]
pub enum SortByParam {
LastUpdated,
Title,
Comment,
Views,
}
impl Default for SortByParam {
fn default() -> Self {
SortByParam::Title
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum SortOrderParam {
Asc,
Desc,
}
impl Default for SortOrderParam {
fn default() -> Self {
SortOrderParam::Asc
}
}
}
#[cfg(feature = "extensions")]
pub mod extensions {
use crate::model::{
Chapter, Manga, SortByParam, SortOrderParam, Source, SourceLogin, SourceLoginResult
};
use anyhow::{anyhow, Result};
use serde_yaml;
use std::io::Read;
pub trait Extension: Send + Sync {
fn detail(&self) -> Source;
fn get_mangas(
&self,
keyword: Option<String>,
genres: Option<Vec<String>>,
page: Option<i32>,
sort_by: Option<SortByParam>,
sort_order: Option<SortOrderParam>,
auth: Option<String>,
) -> Result<Vec<Manga>>;
fn get_manga_info(&self, path: &String) -> Result<Manga>;
fn get_chapters(&self, path: &String) -> Result<Vec<Chapter>>;
fn get_pages(&self, path: &String) -> Result<Vec<String>>;
fn get_page(&self, url: &String) -> Result<Vec<u8>> {
let bytes = {
let resp = ureq::get(url).call()?;
let mut reader = resp.into_reader();
let mut bytes = vec![];
if reader.read_to_end(&mut bytes).is_err() {
return Err(anyhow!("error read image"));
}
bytes
};
Ok(bytes)
}
fn login(&self, _: SourceLogin) -> Result<SourceLoginResult> {
Err(anyhow!("not implemented"))
}
}
pub struct PluginDeclaration {
pub rustc_version: &'static str,
pub core_version: &'static str,
pub name: &'static str,
pub register: unsafe fn(&mut dyn PluginRegistrar, Option<&serde_yaml::Value>),
}
pub trait PluginRegistrar {
fn register_function(&mut self, name: &str, extension: Box<dyn Extension>);
}
#[macro_export]
macro_rules! export_plugin {
($name:expr, $register:expr) => {
#[doc(hidden)]
#[no_mangle]
pub static plugin_declaration: $crate::extensions::PluginDeclaration =
$crate::extensions::PluginDeclaration {
rustc_version: $crate::RUSTC_VERSION,
core_version: $crate::CORE_VERSION,
name: $name,
register: $register,
};
};
}
}