#![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 manga {
use chrono::Local;
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct SourceIndex {
pub name: String,
pub path: String,
pub rustc_version: String,
pub core_version: String,
#[serde(default)]
pub installed_version: String,
pub version: String,
#[serde(default)]
pub installed: bool,
#[serde(default)]
pub update: bool,
}
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct SourceLogin {
pub username: String,
pub password: String,
pub remember_me: Option<bool>,
pub two_factor: Option<String>,
}
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct SourceLoginResult {
pub source_name: String,
pub auth_type: String,
pub value: String,
}
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct Source {
pub name: String,
pub url: String,
pub version: String,
}
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct Manga {
pub id: i32,
pub source: String,
pub title: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub author: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub genre: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub path: String,
pub thumbnail_url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_read: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_page: Option<i32>,
pub is_favorite: bool,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Chapter {
pub id: i32,
pub source: String,
pub manga_id: i32,
#[serde(skip_serializing_if = "Option::is_none")]
pub vol: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub no: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
pub path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub read: Option<i32>,
pub uploaded: chrono::NaiveDateTime,
}
impl Default for Chapter {
fn default() -> Self {
Chapter {
id: 0,
source: "".to_string(),
manga_id: 0,
vol: None,
no: None,
title: None,
path: "".to_string(),
read: None,
uploaded: chrono::NaiveDateTime::from_timestamp(0, 0),
}
}
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Image {
pub source_id: i32,
pub source_name: String,
pub path: String,
pub file_name: String,
pub url: String,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
pub enum SortByParam {
LastUpdated,
Title,
Comment,
Views,
}
impl Default for SortByParam {
fn default() -> Self {
SortByParam::Title
}
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
pub enum SortOrderParam {
Asc,
Desc,
}
impl Default for SortOrderParam {
fn default() -> Self {
SortOrderParam::Asc
}
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Params {
pub keyword: Option<String>,
pub genres: Option<Vec<String>>,
pub page: Option<String>,
pub sort_by: Option<SortByParam>,
pub sort_order: Option<SortOrderParam>,
pub refresh: Option<bool>,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct GetParams {
pub refresh: Option<bool>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct History {
pub manga_id: i32,
pub title: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub thumbnail_url: Option<String>,
pub chapter: String,
pub chapter_id: i32,
pub read: i32,
pub at: chrono::NaiveDateTime,
#[serde(skip_serializing_if = "Option::is_none")]
pub days: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub show_sep: Option<bool>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Update {
pub manga_id: i32,
pub title: String,
pub thumbnail_url: String,
pub number: String,
pub chapter_id: i32,
pub uploaded: chrono::NaiveDateTime,
pub days: Option<i64>,
pub show_sep: Option<bool>,
}
impl Default for Update {
fn default() -> Self {
Update {
manga_id: 0,
title: "".to_string(),
thumbnail_url: "".to_string(),
number: "".to_string(),
chapter_id: 0,
uploaded: Local::now().naive_local(),
days: None,
show_sep: None,
}
}
}
}
#[cfg(feature = "rest")]
pub mod rest {
use crate::manga::{Chapter, History, Manga, Source, SourceIndex, Update};
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct GetSourceIndexResponse {
pub sources: Vec<SourceIndex>,
pub status: String,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct GetSourceResponse {
pub sources: Vec<Source>,
pub status: String,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct GetMangasResponse {
pub mangas: Vec<Manga>,
pub status: String,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct GetMangaResponse {
pub manga: Manga,
pub status: String,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct GetChaptersResponse {
pub chapters: Vec<Chapter>,
pub status: String,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct GetPagesResponse {
pub manga_id: i32,
pub pages: Vec<String>,
pub status: String,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ReadResponse {
pub manga: Manga,
pub chapters: Vec<Chapter>,
pub chapter: Chapter,
pub pages: Vec<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct HistoryRequest {
pub chapter_id: i32,
pub read: i32,
pub at: chrono::NaiveDateTime,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct HistoryResponse {
pub history: Vec<History>,
pub status: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UpdatesResponse {
pub updates: Vec<Update>,
pub status: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AddFavoritesResponse {
pub status: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ErrorResponse {
pub message: String,
}
}
#[cfg(feature = "extensions")]
pub mod extensions {
use crate::manga::{Chapter, Manga, Params, Source, SourceLogin, SourceLoginResult};
use anyhow::{anyhow, Result};
use serde_yaml;
use std::io::Read;
pub trait Extension: Send + Sync {
fn info(&self) -> Source;
fn get_mangas(&self, param: Params, auth: 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,
};
};
}
}