novel_cli/utils/
image.rs

1use std::fs;
2use std::path::{Path, PathBuf};
3
4use color_eyre::eyre::{self, Result};
5use image::{ColorType, DynamicImage, ImageReader};
6use ratatui::crossterm::terminal;
7
8pub fn convert_image_ext<T>(image_path: T) -> Result<PathBuf>
9where
10    T: AsRef<Path>,
11{
12    let image_path = image_path.as_ref();
13
14    if !image_path.is_file() {
15        eyre::bail!("Image does not exist: {}", image_path.display());
16    }
17
18    let image_path = dunce::canonicalize(image_path)?;
19
20    let image_ext = image_path.extension().unwrap().to_str().unwrap();
21    if image_ext == "webp" {
22        return Ok(image_path);
23    }
24
25    let image = ImageReader::open(&image_path)?
26        .with_guessed_format()?
27        .decode()?;
28
29    match new_image_ext(&image) {
30        Ok(new_image_ext) => {
31            if image_ext != new_image_ext {
32                let new_image_path = image_path.with_extension(new_image_ext);
33                tracing::info!(
34                    "Perform image format conversion: from `{}` to `{}`",
35                    image_path.display(),
36                    new_image_path.display()
37                );
38
39                if new_image_ext == "webp" {
40                    novel_api::save_as_webp(&image, 75.0, &new_image_path)?;
41                } else {
42                    image.save(&new_image_path)?;
43                }
44
45                Ok(new_image_path)
46            } else {
47                Ok(image_path)
48            }
49        }
50        Err(err) => {
51            tracing::error!("Failed to convert image: {err}");
52            Ok(image_path)
53        }
54    }
55}
56
57pub fn convert_image_file_stem<T, E>(image_path: T, new_image_stem: E) -> Result<PathBuf>
58where
59    T: AsRef<Path>,
60    E: AsRef<str>,
61{
62    let image_path = image_path.as_ref();
63
64    if !image_path.is_file() {
65        eyre::bail!("Image does not exist: {}", image_path.display());
66    }
67
68    let image_path = dunce::canonicalize(image_path)?;
69    let image_dir = image_path.parent().unwrap();
70    let image_file_stem = image_path.file_stem().unwrap().to_str().unwrap();
71    let image_ext = image_path.extension().unwrap().to_str().unwrap();
72
73    if new_image_stem.as_ref() != image_file_stem {
74        let new_image_path = image_dir.join(format!("{}.{image_ext}", new_image_stem.as_ref()));
75
76        tracing::info!(
77            "Perform image copy: from `{}` to `{}`",
78            image_path.display(),
79            new_image_path.display()
80        );
81
82        fs::copy(image_path, &new_image_path)?;
83
84        Ok(new_image_path)
85    } else {
86        Ok(image_path)
87    }
88}
89
90pub fn new_image_ext(image: &DynamicImage) -> Result<&'static str> {
91    match image.color() {
92        ColorType::Rgb8 | ColorType::Rgba8 => Ok("webp"),
93        ColorType::L8
94        | ColorType::L16
95        | ColorType::La8
96        | ColorType::La16
97        | ColorType::Rgb16
98        | ColorType::Rgba16 => Ok("png"),
99        other => eyre::bail!("This color type is not supported: {other:?}"),
100    }
101}
102
103pub fn terminal_size() -> (u16, u16) {
104    terminal::size().unwrap_or((80, 24))
105}