trnovel 0.9.0

Terminal reader for novel
Documentation
use crate::{Result, history::HistoryItem};
use anyhow::anyhow;
use serde::{Deserialize, Serialize};
use std::ops::{Deref, DerefMut};

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct NovelChapters<T> {
    pub current_chapter: usize,
    pub line_percent: f64,
    pub chapters: Option<Vec<T>>,
}

impl<T> NovelChapters<T>
where
    T: Clone,
{
    pub fn new() -> Self {
        Self {
            current_chapter: 0,
            line_percent: 0.0,
            chapters: None,
        }
    }
}

pub trait Novel: Deref<Target = NovelChapters<Self::Chapter>> + DerefMut + Sized + Clone {
    type Chapter: Sync + Send + Clone;
    type Args: Sync + Send + Clone;

    fn init(args: Self::Args) -> impl Future<Output = Result<Self>> + Send + Sync;

    fn set_chapters(&mut self, chapters: &[Self::Chapter]) {
        self.chapters = Some(chapters.to_vec());
    }

    fn get_current_chapter(&self) -> Result<Self::Chapter> {
        Ok(self
            .chapters
            .as_ref()
            .ok_or(anyhow!("章节列表为空"))?
            .get(self.current_chapter)
            .ok_or(anyhow!("当前章节不存在"))?
            .clone())
    }

    fn chapter_percent(&self) -> Result<f64> {
        Ok(self.current_chapter as f64 / self.get_chapters_result()?.len() as f64 * 100.0)
    }

    fn get_chapters_result(&self) -> Result<&Vec<Self::Chapter>> {
        self.chapters.as_ref().ok_or(anyhow!("没有章节信息").into())
    }

    fn get_chapters(&self) -> Option<&Vec<Self::Chapter>> {
        self.chapters.as_ref()
    }

    fn next_chapter(&mut self) -> Result<()> {
        if self.current_chapter + 1 >= self.get_chapters_result()?.len() {
            Err("已经是最后一章了".into())
        } else {
            self.current_chapter += 1;
            Ok(())
        }
    }

    fn set_chapter(&mut self, chapter: usize) -> Result<()> {
        if chapter >= self.get_chapters_result()?.len() {
            Err("章节不存在".into())
        } else {
            if self.current_chapter != chapter {
                self.current_chapter = chapter;
            }
            Ok(())
        }
    }

    fn prev_chapter(&mut self) -> Result<()> {
        if self.current_chapter == 0 {
            Err("已经是第一章了".into())
        } else {
            self.current_chapter -= 1;
            Ok(())
        }
    }
    // 下面的逻辑需要根据Self::Chapter实现,上面的是通用的,可以直接使用NovelChapters的方法

    fn get_chapters_names(&self) -> Result<Vec<(String, usize)>>;

    fn get_content(&self) -> impl Future<Output = Result<String>> + Send + Sync;

    fn request_chapters(&self) -> impl Future<Output = Result<Vec<Self::Chapter>>> + Send + Sync;

    fn get_current_chapter_name(&self) -> Result<String>;

    fn to_history_item(&self) -> Result<HistoryItem>;

    fn get_id(&self) -> String;
}