use std::{
io::{self, Write},
path::PathBuf,
};
use clap::{CommandFactory, Parser, ValueEnum, ValueHint};
use clap_complete::Generator;
use csscolorparser::Color;
use image::{ImageError, ImageFormat, imageops::FilterType};
#[derive(Debug, Parser)]
#[allow(clippy::doc_markdown)]
#[command(version, about, max_term_width(100), arg_required_else_help(true))]
pub struct Opt {
#[arg(
short,
long,
default_value("."),
value_name("PATH"),
value_hint(ValueHint::DirPath)
)]
pub output: PathBuf,
#[arg(long)]
pub png: bool,
#[arg(long, default_value_t)]
pub name: String,
#[arg(long, value_name("NAME"))]
pub short_name: Option<String>,
#[arg(long, default_value("#ffffff"), value_name("COLOR"))]
pub theme_color: Color,
#[arg(long, default_value("#ffffff"), value_name("COLOR"))]
pub background_color: Color,
#[arg(long, value_enum, default_value_t, ignore_case(true))]
pub filter: Filter,
#[arg(short, long, value_enum, ignore_case(true))]
pub format: Option<Format>,
#[arg(long, value_enum, value_name("SHELL"))]
pub generate_completion: Option<Shell>,
#[arg(value_name("IMAGE"), value_hint(ValueHint::FilePath))]
pub input: Option<PathBuf>,
}
impl Opt {
pub fn print_completion(generator: impl Generator) {
clap_complete::generate(
generator,
&mut Self::command(),
Self::command().get_name(),
&mut io::stdout(),
);
}
}
#[derive(Clone, Debug, ValueEnum)]
#[allow(clippy::doc_markdown)]
#[value(rename_all = "lower")]
pub enum Shell {
Bash,
Elvish,
Fish,
Nushell,
#[allow(clippy::enum_variant_names)]
PowerShell,
Zsh,
}
impl Generator for Shell {
fn file_name(&self, name: &str) -> String {
match self {
Self::Bash => clap_complete::Shell::Bash.file_name(name),
Self::Elvish => clap_complete::Shell::Elvish.file_name(name),
Self::Fish => clap_complete::Shell::Fish.file_name(name),
Self::Nushell => clap_complete_nushell::Nushell.file_name(name),
Self::PowerShell => clap_complete::Shell::PowerShell.file_name(name),
Self::Zsh => clap_complete::Shell::Zsh.file_name(name),
}
}
fn generate(&self, cmd: &clap::Command, buf: &mut dyn Write) {
match self {
Self::Bash => clap_complete::Shell::Bash.generate(cmd, buf),
Self::Elvish => clap_complete::Shell::Elvish.generate(cmd, buf),
Self::Fish => clap_complete::Shell::Fish.generate(cmd, buf),
Self::Nushell => clap_complete_nushell::Nushell.generate(cmd, buf),
Self::PowerShell => clap_complete::Shell::PowerShell.generate(cmd, buf),
Self::Zsh => clap_complete::Shell::Zsh.generate(cmd, buf),
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq, ValueEnum)]
#[value(rename_all = "lower")]
pub enum Filter {
Nearest,
Triangle,
#[default]
CatmullRom,
Gaussian,
Lanczos3,
}
impl From<Filter> for FilterType {
fn from(filter: Filter) -> Self {
match filter {
Filter::Nearest => Self::Nearest,
Filter::Triangle => Self::Triangle,
Filter::CatmullRom => Self::CatmullRom,
Filter::Gaussian => Self::Gaussian,
Filter::Lanczos3 => Self::Lanczos3,
}
}
}
#[derive(Clone, Debug, ValueEnum)]
#[allow(clippy::doc_markdown)]
#[value(rename_all = "lower")]
pub enum Format {
Bmp,
#[cfg(feature = "dds")]
Dds,
#[cfg(feature = "ff")]
Farbfeld,
#[cfg(feature = "gif")]
Gif,
#[cfg(feature = "hdr")]
Hdr,
Ico,
#[cfg(feature = "jpeg")]
Jpeg,
#[cfg(feature = "exr")]
OpenExr,
Png,
#[cfg(feature = "pnm")]
Pnm,
#[cfg(feature = "qoi")]
Qoi,
#[cfg(feature = "tga")]
Tga,
#[cfg(feature = "tiff")]
Tiff,
#[cfg(feature = "webp")]
WebP,
#[cfg(feature = "xbm")]
Xbm,
}
impl TryFrom<Format> for ImageFormat {
type Error = ImageError;
fn try_from(format: Format) -> Result<Self, Self::Error> {
match format {
Format::Bmp => Ok(Self::Bmp),
#[cfg(feature = "dds")]
Format::Dds => Ok(Self::Dds),
#[cfg(feature = "ff")]
Format::Farbfeld => Ok(Self::Farbfeld),
#[cfg(feature = "gif")]
Format::Gif => Ok(Self::Gif),
#[cfg(feature = "hdr")]
Format::Hdr => Ok(Self::Hdr),
Format::Ico => Ok(Self::Ico),
#[cfg(feature = "jpeg")]
Format::Jpeg => Ok(Self::Jpeg),
#[cfg(feature = "exr")]
Format::OpenExr => Ok(Self::OpenExr),
Format::Png => Ok(Self::Png),
#[cfg(feature = "pnm")]
Format::Pnm => Ok(Self::Pnm),
#[cfg(feature = "qoi")]
Format::Qoi => Ok(Self::Qoi),
#[cfg(feature = "tga")]
Format::Tga => Ok(Self::Tga),
#[cfg(feature = "tiff")]
Format::Tiff => Ok(Self::Tiff),
#[cfg(feature = "webp")]
Format::WebP => Ok(Self::WebP),
#[cfg(feature = "xbm")]
Format::Xbm => Err(Self::Error::Unsupported(
image::error::ImageFormatHint::Unknown.into(),
)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn verify_app() {
Opt::command().debug_assert();
}
#[test]
fn file_name_shell() {
assert_eq!(Shell::Bash.file_name("favico"), "favico.bash");
assert_eq!(Shell::Elvish.file_name("favico"), "favico.elv");
assert_eq!(Shell::Fish.file_name("favico"), "favico.fish");
assert_eq!(Shell::Nushell.file_name("favico"), "favico.nu");
assert_eq!(Shell::PowerShell.file_name("favico"), "_favico.ps1");
assert_eq!(Shell::Zsh.file_name("favico"), "_favico");
}
#[test]
fn default_filter() {
assert_eq!(Filter::default(), Filter::CatmullRom);
}
#[test]
fn from_filter_to_filter_type() {
assert_eq!(FilterType::from(Filter::Nearest), FilterType::Nearest);
assert_eq!(FilterType::from(Filter::Triangle), FilterType::Triangle);
assert_eq!(FilterType::from(Filter::CatmullRom), FilterType::CatmullRom);
assert_eq!(FilterType::from(Filter::Gaussian), FilterType::Gaussian);
assert_eq!(FilterType::from(Filter::Lanczos3), FilterType::Lanczos3);
}
#[test]
fn try_from_format_to_image_format() {
assert_eq!(
ImageFormat::try_from(Format::Bmp).unwrap(),
ImageFormat::Bmp
);
#[cfg(feature = "dds")]
assert_eq!(
ImageFormat::try_from(Format::Dds).unwrap(),
ImageFormat::Dds
);
#[cfg(feature = "ff")]
assert_eq!(
ImageFormat::try_from(Format::Farbfeld).unwrap(),
ImageFormat::Farbfeld
);
#[cfg(feature = "gif")]
assert_eq!(
ImageFormat::try_from(Format::Gif).unwrap(),
ImageFormat::Gif
);
#[cfg(feature = "hdr")]
assert_eq!(
ImageFormat::try_from(Format::Hdr).unwrap(),
ImageFormat::Hdr
);
assert_eq!(
ImageFormat::try_from(Format::Ico).unwrap(),
ImageFormat::Ico
);
#[cfg(feature = "jpeg")]
assert_eq!(
ImageFormat::try_from(Format::Jpeg).unwrap(),
ImageFormat::Jpeg
);
#[cfg(feature = "exr")]
assert_eq!(
ImageFormat::try_from(Format::OpenExr).unwrap(),
ImageFormat::OpenExr
);
assert_eq!(
ImageFormat::try_from(Format::Png).unwrap(),
ImageFormat::Png
);
#[cfg(feature = "pnm")]
assert_eq!(
ImageFormat::try_from(Format::Pnm).unwrap(),
ImageFormat::Pnm
);
#[cfg(feature = "qoi")]
assert_eq!(
ImageFormat::try_from(Format::Qoi).unwrap(),
ImageFormat::Qoi
);
#[cfg(feature = "tga")]
assert_eq!(
ImageFormat::try_from(Format::Tga).unwrap(),
ImageFormat::Tga
);
#[cfg(feature = "tiff")]
assert_eq!(
ImageFormat::try_from(Format::Tiff).unwrap(),
ImageFormat::Tiff
);
#[cfg(feature = "webp")]
assert_eq!(
ImageFormat::try_from(Format::WebP).unwrap(),
ImageFormat::WebP
);
#[cfg(feature = "xbm")]
assert!(ImageFormat::try_from(Format::Xbm).is_err());
}
}