novel-cli 0.17.0

A set of tools for downloading novels from the web, manipulating text, and generating EPUB
Documentation
use std::fmt::Write;
use std::sync::Arc;

use color_eyre::eyre::{self, Result};
use comfy_table::modifiers::UTF8_ROUND_CORNERS;
use comfy_table::presets::UTF8_FULL;
use comfy_table::{Cell, CellAlignment, ContentArrangement, Table};
use novel_api::{Client, NovelInfo};

use crate::cmd::Convert;
use crate::utils;

pub async fn novel_info<T>(client: &Arc<T>, novel_id: u32) -> Result<NovelInfo>
where
    T: Client + Send + Sync + 'static,
{
    let novel_info = client.novel_info(novel_id).await?;
    eyre::ensure!(
        novel_info.is_some(),
        "The novel does not exist: `{}`",
        novel_id
    );

    Ok(novel_info.unwrap())
}

pub fn novel_info_to_string<T>(novel_info: &NovelInfo, converts: T) -> Result<String>
where
    T: AsRef<[Convert]>,
{
    let mut result = String::with_capacity(256);

    let converts = converts.as_ref();

    writeln!(
        result,
        "{}{}",
        utils::convert_str("名字", converts, false)?,
        utils::convert_str(&novel_info.name, converts, false)?
    )?;

    writeln!(
        result,
        "{}{}",
        utils::convert_str("作者", converts, false)?,
        utils::convert_str(&novel_info.author_name, converts, false)?
    )?;

    if novel_info.category.is_some() {
        writeln!(
            result,
            "{}{}",
            utils::convert_str("类型", converts, false)?,
            utils::convert_str(try_get_category(novel_info), converts, false)?
        )?;
    }

    if novel_info.tags.is_some() {
        writeln!(
            result,
            "{}{}",
            utils::convert_str("标签", converts, false)?,
            utils::convert_str(try_get_tags(novel_info), converts, false)?
        )?;
    }

    if novel_info.word_count.is_some() {
        writeln!(
            result,
            "{}{}",
            utils::convert_str("字数", converts, false)?,
            utils::convert_str(try_get_word_count(novel_info), converts, false)?
        )?;
    }

    if novel_info.is_finished.is_some() {
        writeln!(
            result,
            "{}{}",
            utils::convert_str("状态", converts, false)?,
            utils::convert_str(try_get_is_finished(novel_info), converts, false)?
        )?;
    }

    if novel_info.create_time.is_some() {
        writeln!(
            result,
            "{}{}",
            utils::convert_str("创建时间", converts, false)?,
            try_get_create_time(novel_info)
        )?;
    }

    if novel_info.update_time.is_some() {
        writeln!(
            result,
            "{}{}",
            utils::convert_str("更新时间", converts, false)?,
            try_get_update_time(novel_info)
        )?;
    }

    if novel_info.introduction.is_some() {
        writeln!(
            result,
            "{}{}",
            utils::convert_str("简介", converts, false)?,
            utils::convert_str(try_get_introduction(novel_info), converts, false)?
        )?;
    }

    Ok(result)
}

pub fn print_novel_infos<T>(novel_infos: Vec<NovelInfo>, converts: T) -> Result<()>
where
    T: AsRef<[Convert]>,
{
    let converts = converts.as_ref();

    let mut row = vec![
        utils::convert_str("序号", converts, false)?,
        utils::convert_str("编号", converts, false)?,
        utils::convert_str("名字", converts, false)?,
        utils::convert_str("作者", converts, false)?,
    ];

    let category = novel_infos
        .iter()
        .any(|novel_info| novel_info.category.is_some());
    if category {
        row.push(utils::convert_str("类型", converts, false)?);
    }

    let tags = novel_infos
        .iter()
        .any(|novel_info| novel_info.tags.is_some());
    if tags {
        row.push(utils::convert_str("标签", converts, false)?);
    }

    let word_count = novel_infos
        .iter()
        .any(|novel_info| novel_info.word_count.is_some());
    if word_count {
        row.push(utils::convert_str("字数", converts, false)?);
    }

    let is_finished = novel_infos
        .iter()
        .any(|novel_info| novel_info.is_finished.is_some());
    if is_finished {
        row.push(utils::convert_str("状态", converts, false)?);
    }

    let create_time = novel_infos
        .iter()
        .any(|novel_info| novel_info.create_time.is_some());
    if create_time {
        row.push(utils::convert_str("创建时间", converts, false)?);
    }

    let update_time = novel_infos
        .iter()
        .any(|novel_info| novel_info.update_time.is_some());
    if update_time {
        row.push(utils::convert_str("更新时间", converts, false)?);
    }

    let mut table = Table::new();
    table
        .load_preset(UTF8_FULL)
        .apply_modifier(UTF8_ROUND_CORNERS)
        .set_content_arrangement(ContentArrangement::Dynamic)
        .set_header(row);

    let mut index = 1;
    for novel_info in novel_infos {
        let mut row = vec![
            Cell::new(index.to_string()),
            Cell::new(novel_info.id.to_string()),
            Cell::new(utils::convert_str(&novel_info.name, converts, false)?),
            Cell::new(utils::convert_str(
                &novel_info.author_name,
                converts,
                false,
            )?),
        ];

        if category {
            row.push(Cell::new(utils::convert_str(
                try_get_category(&novel_info),
                converts,
                false,
            )?));
        }

        if tags {
            row.push(Cell::new(utils::convert_str(
                try_get_tags(&novel_info),
                converts,
                false,
            )?));
        }

        if word_count {
            row.push(
                Cell::new(utils::convert_str(
                    try_get_word_count(&novel_info),
                    converts,
                    false,
                )?)
                .set_alignment(CellAlignment::Right),
            );
        }

        if is_finished {
            row.push(Cell::new(utils::convert_str(
                try_get_is_finished(&novel_info),
                converts,
                false,
            )?));
        }

        if create_time {
            row.push(Cell::new(try_get_create_time(&novel_info)));
        }

        if update_time {
            row.push(Cell::new(try_get_update_time(&novel_info)));
        }

        table.add_row(row);

        index += 1;
    }

    println!("{table}");

    Ok(())
}

#[must_use]
fn try_get_introduction(novel_info: &NovelInfo) -> String {
    if let Some(introduction) = &novel_info.introduction {
        introduction.join("\n")
    } else {
        String::default()
    }
}

#[must_use]
fn try_get_word_count(novel_info: &NovelInfo) -> String {
    if let Some(word_count) = &novel_info.word_count {
        if *word_count >= 10000 {
            format!("{}", word_count / 10000)
        } else {
            word_count.to_string()
        }
    } else {
        String::default()
    }
}

#[must_use]
fn try_get_is_finished(novel_info: &NovelInfo) -> String {
    if novel_info.is_finished.is_some() {
        if novel_info.is_finished.unwrap() {
            String::from("已完结")
        } else {
            String::from("未完结")
        }
    } else {
        String::default()
    }
}

#[must_use]
fn try_get_create_time(novel_info: &NovelInfo) -> String {
    if let Some(create_time) = &novel_info.create_time {
        create_time.to_string()
    } else {
        String::default()
    }
}

#[must_use]
fn try_get_update_time(novel_info: &NovelInfo) -> String {
    if let Some(update_time) = novel_info.update_time {
        update_time.to_string()
    } else {
        String::default()
    }
}

#[must_use]
fn try_get_category(novel_info: &NovelInfo) -> String {
    if let Some(category) = &novel_info.category {
        category.name.to_string()
    } else {
        String::default()
    }
}

#[must_use]
fn try_get_tags(novel_info: &NovelInfo) -> String {
    if let Some(tags) = &novel_info.tags {
        tags.iter()
            .map(|tag| tag.name.to_string())
            .collect::<Vec<String>>()
            .join("")
    } else {
        String::default()
    }
}