novel-cli 0.17.0

A set of tools for downloading novels from the web, manipulating text, and generating EPUB
Documentation
use std::fs;
use std::path::{Path, PathBuf};

use color_eyre::eyre::{self, Result};
use image::{ColorType, DynamicImage, ImageReader};
use ratatui::crossterm::terminal;

pub fn convert_image_ext<T>(image_path: T) -> Result<PathBuf>
where
    T: AsRef<Path>,
{
    let image_path = image_path.as_ref();

    if !image_path.is_file() {
        eyre::bail!("Image does not exist: {}", image_path.display());
    }

    let image_path = dunce::canonicalize(image_path)?;

    let image_ext = image_path.extension().unwrap().to_str().unwrap();
    if image_ext == "webp" {
        return Ok(image_path);
    }

    let image = ImageReader::open(&image_path)?
        .with_guessed_format()?
        .decode()?;

    match new_image_ext(&image) {
        Ok(new_image_ext) => {
            if image_ext != new_image_ext {
                let new_image_path = image_path.with_extension(new_image_ext);
                tracing::info!(
                    "Perform image format conversion: from `{}` to `{}`",
                    image_path.display(),
                    new_image_path.display()
                );

                if new_image_ext == "webp" {
                    novel_api::save_as_webp(&image, 75.0, &new_image_path)?;
                } else {
                    image.save(&new_image_path)?;
                }

                Ok(new_image_path)
            } else {
                Ok(image_path)
            }
        }
        Err(err) => {
            tracing::error!("Failed to convert image: {err}");
            Ok(image_path)
        }
    }
}

pub fn convert_image_file_stem<T, E>(image_path: T, new_image_stem: E) -> Result<PathBuf>
where
    T: AsRef<Path>,
    E: AsRef<str>,
{
    let image_path = image_path.as_ref();

    if !image_path.is_file() {
        eyre::bail!("Image does not exist: {}", image_path.display());
    }

    let image_path = dunce::canonicalize(image_path)?;
    let image_dir = image_path.parent().unwrap();
    let image_file_stem = image_path.file_stem().unwrap().to_str().unwrap();
    let image_ext = image_path.extension().unwrap().to_str().unwrap();

    if new_image_stem.as_ref() != image_file_stem {
        let new_image_path = image_dir.join(format!("{}.{image_ext}", new_image_stem.as_ref()));

        tracing::info!(
            "Perform image copy: from `{}` to `{}`",
            image_path.display(),
            new_image_path.display()
        );

        fs::copy(image_path, &new_image_path)?;

        Ok(new_image_path)
    } else {
        Ok(image_path)
    }
}

pub fn new_image_ext(image: &DynamicImage) -> Result<&'static str> {
    match image.color() {
        ColorType::Rgb8 | ColorType::Rgba8 => Ok("webp"),
        ColorType::L8
        | ColorType::L16
        | ColorType::La8
        | ColorType::La16
        | ColorType::Rgb16
        | ColorType::Rgba16 => Ok("png"),
        other => eyre::bail!("This color type is not supported: {other:?}"),
    }
}

pub fn terminal_size() -> (u16, u16) {
    terminal::size().unwrap_or((80, 24))
}