parse-book-source 0.7.0

Terminal reader for novel
Documentation
//! 领域类型(纯数据,无 IO、无规则逻辑)。

use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

/// 一个 explore 入口(运行时):展示标题 + 取页变量。由入口加载(`Engine::explore_entries`)
/// 从静态/动态入口源产生,供 UI 选择;取页时与 base/page/pageSize 合并后运行 `explore.page`。
///
/// 入口身份是「标题 + 变量」而非固定 URL——取页 URL 由 `explore.page.request` 用这些变量与
/// `{{page}}` 模板生成。
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ExploreEntry {
    pub title: String,
    pub vars: BTreeMap<String, String>,
}

/// 目录条目(章节;`is_volume` 为 true 时表示卷标题,不是可阅读章节)。
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct Chapter {
    pub title: String,
    pub url: String,
    #[serde(default)]
    pub is_volume: bool,
}

/// 卷标记(分卷元数据):卷标题 + 其首章在扁平章节列表中的索引。
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct Volume {
    pub title: String,
    pub first_chapter_index: usize,
}

/// 解析后的目录:扁平章节列表 + 平行卷元数据(卷不进入章节序列)。
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Toc {
    pub chapters: Vec<Chapter>,
    pub volumes: Vec<Volume>,
}

/// 书籍详情。
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct BookInfo {
    pub name: String,
    pub author: String,
    pub cover: String,
    pub intro: String,
    pub kind: String,
    pub last_chapter: String,
    pub toc_url: String,
    pub word_count: String,
}

/// 搜索/浏览结果中的一本书(书籍详情 + 入口 URL)。
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct BookListItem {
    #[serde(flatten)]
    pub info: BookInfo,
    pub book_url: String,
}

/// 搜索/浏览**一页**的结果:书列表 + 可选边界信号。`explore`/`search` 的返回类型。
///
/// - `total_pages`(`render-dual-source`):精确总页数,进度「第 N / M 页」;
/// - `has_more`(`list-has-more`):是否还有下一页,UI 据此到头停翻。
///
/// 二者书源未配或取不到则为 `None`(UI 不限制 / 不显示)。
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct BookList {
    pub items: Vec<BookListItem>,
    pub total_pages: Option<u32>,
    pub has_more: Option<bool>,
}

impl BookList {
    /// 仅书列表、无边界信号(非 render / 未配规则的现状路径)。
    pub fn new(items: Vec<BookListItem>) -> Self {
        Self {
            items,
            total_pages: None,
            has_more: None,
        }
    }
}