use image::{DynamicImage, ImageFormat};
use std::fmt;
use std::fmt::{Debug, Display};
use std::str::FromStr;
mod crop;
pub use crop::*;
mod maxdim;
pub use maxdim::*;
pub trait ImageTransformer
where
Self: PartialEq,
Self: Sized + Clone,
Self: Display + Debug,
{
fn transform(&self, input: &mut DynamicImage);
fn parse_args(args: &str) -> Result<Self, String>;
}
use serde::{Deserialize, Deserializer};
#[derive(Debug, Clone, PartialEq)]
pub enum TransformerEnum {
MaxDim(MaxDimTransformer),
Crop(CropTransformer),
Format {
format: ImageFormat,
},
}
impl FromStr for TransformerEnum {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
let (name, args) = {
let name_len = match s.find('(') {
Some(x) => x + 1,
None => {
return Err(format!(
"invalid transformation {s}. Must look like name(args)."
));
}
};
let mut balance = 1;
let mut end = name_len;
for i in s[name_len..].bytes() {
match i {
b')' => balance -= 1,
b'(' => balance += 1,
_ => {}
}
if balance == 0 {
break;
}
end += 1;
}
if balance != 0 {
return Err(format!("mismatched parenthesis in {s}"));
}
let name = s[0..name_len - 1].trim();
let args = s[name_len..end].trim();
let trail = s[end + 1..].trim();
if !trail.is_empty() {
return Err(format!(
"invalid transformation {s}. Must look like name(args)."
));
}
(name, args)
};
match name {
"maxdim" => Ok(Self::MaxDim(MaxDimTransformer::parse_args(args)?)),
"crop" => Ok(Self::Crop(CropTransformer::parse_args(args)?)),
"format" => Ok(TransformerEnum::Format {
format: ImageFormat::from_extension(args)
.ok_or(format!("invalid image format {args}"))?,
}),
_ => Err(format!("unknown transformation {name}")),
}
}
}
impl<'de> Deserialize<'de> for TransformerEnum {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
s.parse().map_err(serde::de::Error::custom)
}
}
impl Display for TransformerEnum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TransformerEnum::MaxDim(x) => Display::fmt(x, f),
TransformerEnum::Crop(x) => Display::fmt(x, f),
TransformerEnum::Format { format } => {
write!(f, "format({})", format.extensions_str()[0])
}
}
}
}