mod animation; mod image; pub(crate) mod mimes; mod video; use std::str::FromStr; pub(crate) use animation::{AnimationFormat, AnimationInput, AnimationOutput}; pub(crate) use image::{ImageFormat, ImageInput, ImageOutput}; pub(crate) use video::{InternalVideoFormat, OutputVideoFormat, VideoFormat}; #[derive(Clone, Debug)] pub(crate) struct PrescribedFormats { pub(crate) image: Option, pub(crate) animation: Option, pub(crate) video: Option, pub(crate) allow_audio: bool, } #[derive(Clone, Debug)] pub(crate) enum InputFile { Image(ImageInput), Animation(AnimationInput), Video(VideoFormat), } #[derive(Clone, Debug)] pub(crate) enum OutputFile { Image(ImageOutput), Animation(AnimationOutput), Video(OutputVideoFormat), } #[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)] pub(crate) enum InternalFormat { Image(ImageFormat), Animation(AnimationFormat), Video(InternalVideoFormat), } #[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)] pub(crate) enum ProcessableFormat { Image(ImageFormat), Animation(AnimationFormat), } #[derive( Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, )] pub(crate) enum InputProcessableFormat { #[serde(rename = "apng")] Apng, #[serde(rename = "avif")] Avif, #[serde(rename = "gif")] Gif, #[serde(rename = "jpeg")] Jpeg, #[serde(rename = "jxl")] Jxl, #[serde(rename = "png")] Png, #[serde(rename = "webp")] Webp, } impl InputFile { const fn file_extension(&self) -> &'static str { match self { Self::Image(ImageInput { format, .. }) => format.file_extension(), Self::Animation(AnimationInput { format }) => format.file_extension(), Self::Video(format) => format.file_extension(), } } const fn build_output(&self, prescribed: &PrescribedFormats) -> OutputFile { match (self, prescribed) { (InputFile::Image(input), PrescribedFormats { image, .. }) => { OutputFile::Image(input.build_output(*image)) } (InputFile::Animation(input), PrescribedFormats { animation, .. }) => { OutputFile::Animation(input.build_output(*animation)) } ( InputFile::Video(input), PrescribedFormats { video, allow_audio, .. }, ) => OutputFile::Video(input.build_output(*video, *allow_audio)), } } pub(crate) const fn internal_format(&self) -> InternalFormat { match self { Self::Image(ImageInput { format, .. }) => InternalFormat::Image(*format), Self::Animation(AnimationInput { format }) => InternalFormat::Animation(*format), Self::Video(format) => InternalFormat::Video(format.internal_format()), } } } impl OutputFile { const fn file_extension(&self) -> &'static str { match self { Self::Image(ImageOutput { format, .. }) => format.file_extension(), Self::Animation(AnimationOutput { format, .. }) => format.file_extension(), Self::Video(format) => format.file_extension(), } } pub(crate) const fn internal_format(&self) -> InternalFormat { match self { Self::Image(ImageOutput { format, .. }) => InternalFormat::Image(*format), Self::Animation(AnimationOutput { format, .. }) => InternalFormat::Animation(*format), Self::Video(format) => InternalFormat::Video(format.internal_format()), } } } impl InternalFormat { pub(crate) fn media_type(self) -> mime::Mime { match self { Self::Image(format) => format.media_type(), Self::Animation(format) => format.media_type(), Self::Video(format) => format.media_type(), } } pub(crate) const fn file_extension(self) -> &'static str { match self { Self::Image(format) => format.file_extension(), Self::Animation(format) => format.file_extension(), Self::Video(format) => format.file_extension(), } } pub(crate) const fn processable_format(self) -> Option { match self { Self::Image(format) => Some(ProcessableFormat::Image(format)), Self::Animation(format) => Some(ProcessableFormat::Animation(format)), Self::Video(_) => None, } } } impl ProcessableFormat { pub(crate) const fn file_extension(self) -> &'static str { match self { Self::Image(format) => format.file_extension(), Self::Animation(format) => format.file_extension(), } } pub(crate) const fn coalesce(self) -> bool { matches!(self, Self::Animation(_)) } pub(crate) fn magick_format(self) -> &'static str { match self { Self::Image(format) => format.magick_format(), Self::Animation(format) => format.magick_format(), } } } impl FromStr for InputProcessableFormat { type Err = String; fn from_str(s: &str) -> Result { match s { "apng" => Ok(Self::Apng), "avif" => Ok(Self::Avif), "gif" => Ok(Self::Gif), "jpeg" => Ok(Self::Jpeg), "jpg" => Ok(Self::Jpeg), "jxl" => Ok(Self::Jxl), "png" => Ok(Self::Png), "webp" => Ok(Self::Webp), otherwise => Err(format!("Invalid format: {otherwise}")), } } } impl std::fmt::Display for InputProcessableFormat { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Apng => write!(f, "apng"), Self::Avif => write!(f, "avif"), Self::Gif => write!(f, "gif"), Self::Jpeg => write!(f, "jpeg"), Self::Jxl => write!(f, "jxl"), Self::Png => write!(f, "png"), Self::Webp => write!(f, "webp"), } } }