use std::{
fmt::{self, Display},
ops::{Range, RangeFrom, RangeTo},
path::PathBuf,
};
use chrono::NaiveDateTime;
use image::DynamicImage;
use url::Url;
use crate::Error;
#[must_use]
#[derive(Debug)]
pub struct UserInfo {
pub nickname: String,
pub avatar: Option<Url>,
}
#[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_vip: Option<bool>,
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, PartialEq)]
pub struct Category {
pub id: Option<u16>,
pub parent_id: Option<u16>,
pub name: String,
}
impl Display for Category {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)
}
}
#[must_use]
#[derive(Debug, Clone, PartialEq)]
pub struct Tag {
pub id: Option<u16>,
pub name: String,
}
impl Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)
}
}
pub type VolumeInfos = Vec<VolumeInfo>;
#[must_use]
#[derive(Debug)]
pub struct VolumeInfo {
pub id: u32,
pub title: String,
pub chapter_infos: Vec<ChapterInfo>,
}
#[must_use]
#[derive(Debug, Default)]
pub struct ChapterInfo {
pub novel_id: Option<u32>,
pub id: u32,
pub title: String,
pub is_vip: Option<bool>,
pub price: Option<u16>,
pub payment_required: Option<bool>,
pub is_valid: Option<bool>,
pub word_count: Option<u32>,
pub create_time: Option<NaiveDateTime>,
pub update_time: Option<NaiveDateTime>,
}
impl ChapterInfo {
pub fn payment_required(&self) -> bool {
!self.payment_required.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.payment_required() && self.is_valid()
}
}
pub type ContentInfos = Vec<ContentInfo>;
#[must_use]
#[derive(Debug)]
pub enum ContentInfo {
Text(String),
Image(Url),
}
#[derive(Debug, Default)]
pub struct Options {
pub keyword: Option<String>,
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>),
}
#[derive(Debug)]
pub enum Comment {
Short(ShortComment),
Long(LongComment),
}
#[derive(Debug)]
pub struct ShortComment {
pub id: u32,
pub user: UserInfo,
pub content: Vec<String>,
pub create_time: Option<NaiveDateTime>,
pub like_count: Option<u16>,
pub replies: Option<Vec<ShortComment>>,
}
impl PartialEq for ShortComment {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
#[derive(Debug)]
pub struct LongComment {
pub id: u32,
pub user: UserInfo,
pub title: String,
pub content: Vec<String>,
pub create_time: Option<NaiveDateTime>,
pub like_count: Option<u16>,
pub replies: Option<Vec<ShortComment>>,
}
impl PartialEq for LongComment {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
#[derive(Clone, Copy)]
pub enum CommentType {
Short,
Long,
}
#[trait_variant::make(Send)]
pub trait Client {
fn proxy(&mut self, proxy: Url);
fn no_proxy(&mut self);
fn cert(&mut self, cert_path: PathBuf);
async fn shutdown(&self) -> Result<(), Error>;
async fn add_cookie(&self, cookie_str: &str, url: &Url) -> Result<(), Error>;
async fn log_in(&self, username: String, password: Option<String>) -> Result<(), Error>;
async fn logged_in(&self) -> Result<bool, Error>;
async fn user_info(&self) -> Result<UserInfo, Error>;
async fn money(&self) -> Result<u32, Error>;
async fn sign_in(&self) -> Result<(), Error>;
async fn bookshelf_infos(&self) -> Result<Vec<u32>, Error>;
async fn novel_info(&self, id: u32) -> Result<Option<NovelInfo>, Error>;
async fn comments(
&self,
id: u32,
comment_type: CommentType,
need_replies: bool,
page: u16,
size: u16,
) -> Result<Option<Vec<Comment>>, Error>;
async fn volume_infos(&self, id: u32) -> Result<Option<VolumeInfos>, Error>;
async fn content_infos(&self, info: &ChapterInfo) -> Result<ContentInfos, Error>;
async fn buy_chapter(&self, info: &ChapterInfo) -> Result<(), Error>;
async fn image(&self, url: &Url) -> Result<DynamicImage, Error>;
async fn categories(&self) -> Result<&Vec<Category>, Error>;
async fn tags(&self) -> Result<&Vec<Tag>, Error>;
async fn search_infos(
&self,
option: &Options,
page: u16,
size: u16,
) -> Result<Option<Vec<u32>>, Error>;
fn has_this_type_of_comments(comment_type: CommentType) -> bool;
}