pict-rs/src/formats.rs

197 lines
6 KiB
Rust
Raw Normal View History

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<ImageFormat>,
pub(crate) animation: Option<AnimationFormat>,
pub(crate) video: Option<OutputVideoFormat>,
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<ProcessableFormat> {
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<Self, Self::Err> {
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"),
}
}
}