use std::{
ops::{Range, RangeFrom, RangeTo},
path::Path,
};
use async_trait::async_trait;
use chrono::NaiveDateTime;
use image::DynamicImage;
use url::Url;
use crate::Error;
#[must_use]
#[derive(Debug)]
pub struct UserInfo {
pub nickname: String,
}
#[must_use]
#[derive(Debug, Default)]
pub struct NovelInfo {
pub id: u32,
pub name: String,
pub author_name: String,
pub cover_url: Option<Url>,
pub introduction: Option<Vec<String>>,
pub word_count: Option<u32>,
pub is_finished: Option<bool>,
pub create_time: Option<NaiveDateTime>,
pub update_time: Option<NaiveDateTime>,
pub category: Option<Category>,
pub tags: Option<Vec<Tag>>,
}
impl PartialEq for NovelInfo {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
#[must_use]
#[derive(Debug, Clone)]
pub struct Category {
pub id: Option<u16>,
pub name: String,
}
impl ToString for Category {
fn to_string(&self) -> String {
self.name.to_string()
}
}
#[must_use]
#[derive(Debug, Clone)]
pub struct Tag {
pub id: Option<u16>,
pub name: String,
}
impl ToString for Tag {
fn to_string(&self) -> String {
self.name.to_string()
}
}
pub type VolumeInfos = Vec<VolumeInfo>;
#[must_use]
#[derive(Debug)]
pub struct VolumeInfo {
pub title: String,
pub chapter_infos: Vec<ChapterInfo>,
}
#[must_use]
#[derive(Debug)]
pub struct ChapterInfo {
pub identifier: Identifier,
pub title: String,
pub is_vip: Option<bool>,
pub accessible: Option<bool>,
pub is_valid: Option<bool>,
pub word_count: Option<u16>,
pub update_time: Option<NaiveDateTime>,
}
impl ChapterInfo {
pub fn can_download(&self) -> bool {
!crate::is_some_and(self.accessible.as_ref(), |x| !x)
&& !crate::is_some_and(self.is_valid.as_ref(), |x| !x)
}
}
#[must_use]
#[derive(Debug)]
pub enum Identifier {
Id(u32),
Url(Url),
}
impl ToString for Identifier {
fn to_string(&self) -> String {
match self {
Identifier::Id(id) => id.to_string(),
Identifier::Url(url) => url.to_string(),
}
}
}
pub type ContentInfos = Vec<ContentInfo>;
#[must_use]
#[derive(Debug)]
pub enum ContentInfo {
Text(String),
Image(Url),
}
#[derive(Default)]
pub struct Options {
pub is_finished: Option<bool>,
pub is_vip: Option<bool>,
pub category: Option<Category>,
pub tags: Option<Vec<Tag>>,
pub excluded_tags: Option<Vec<Tag>>,
pub update_days: Option<u8>,
pub word_count: Option<WordCountRange>,
}
pub enum WordCountRange {
Range(Range<u32>),
RangeFrom(RangeFrom<u32>),
RangeTo(RangeTo<u32>),
}
#[async_trait]
pub trait Client {
fn proxy(&mut self, proxy: Url);
fn no_proxy(&mut self);
fn cert<T>(&mut self, cert_path: T)
where
T: AsRef<Path>;
async fn shutdown(&self) -> Result<(), Error>;
async fn add_cookie(&self, cookie_str: &str, url: &Url) -> Result<(), Error>;
async fn login<T, E>(&self, username: T, password: E) -> Result<(), Error>
where
T: AsRef<str> + Send + Sync,
E: AsRef<str> + Send + Sync;
async fn user_info(&self) -> Result<Option<UserInfo>, Error>;
async fn novel_info(&self, id: u32) -> Result<Option<NovelInfo>, Error>;
async fn volume_infos(&self, id: u32) -> Result<VolumeInfos, Error>;
async fn content_infos(&self, info: &ChapterInfo) -> Result<ContentInfos, Error>;
async fn image(&self, url: &Url) -> Result<DynamicImage, Error>;
async fn search_infos<T>(&self, text: T, page: u16, size: u16) -> Result<Vec<u32>, Error>
where
T: AsRef<str> + Send + Sync;
async fn favorite_infos(&self) -> Result<Vec<u32>, Error>;
async fn categories(&self) -> Result<&Vec<Category>, Error>;
async fn tags(&self) -> Result<&Vec<Tag>, Error>;
async fn novels(&self, option: &Options, page: u16, size: u16) -> Result<Vec<u32>, Error>;
}