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 is_accessible: Option<bool>,
pub is_valid: Option<bool>,
pub word_count: Option<u16>,
pub update_time: Option<NaiveDateTime>,
}
impl ChapterInfo {
pub fn is_accessible(&self) -> bool {
!self.is_accessible.as_ref().is_some_and(|x| !x)
}
pub fn is_valid(&self) -> bool {
!self.is_valid.as_ref().is_some_and(|x| !x)
}
pub fn can_download(&self) -> bool {
self.is_accessible() && self.is_valid()
}
}
#[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(Debug, 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>,
}
#[derive(Debug)]
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(&self, username: String, password: String) -> Result<(), Error>;
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 bookshelf_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>;
}