thumbor 0.1.8

A Rust client for the Thumbor image service
Documentation
use crate::geometry::Rect;
use std::fmt;

pub enum Color {
    Rgb(u8, u8, u8),
    Name(String),
}

impl fmt::Display for Color {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Color::Rgb(r, g, b) => write!(f, "#{r:02x}{g:02x}{b:02x}",),
            Color::Name(name) => write!(f, "{name}"),
        }
    }
}

#[derive(strum::AsRefStr)]
#[strum(serialize_all = "lowercase")]
pub enum Format {
    Webp,
    Jpeg,
    Gif,
    Png,
    Avif,
    Heic,
}

pub enum Radius {
    Ellipsis(u32, u32),
    Circle(u32),
}

impl fmt::Display for Radius {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Radius::Circle(radius) => write!(f, "{radius}"),
            Radius::Ellipsis(width, height) => write!(f, "{width}|{height}",),
        }
    }
}

#[derive(strum::AsRefStr)]
#[strum(serialize_all = "lowercase")]
pub enum Filter {
    #[strum(serialize = "autojpg")]
    AutoJPG,
    BackgroundColor(Color),
    Blur {
        radius: u8,
        sigma: Option<u8>,
    },
    Brightness(i8),
    Contrast(i8),
    Convolution {
        matrix_items: Vec<i8>,
        number_of_columns: u8,
        should_normalize: bool,
    },
    Cover,
    Equalize,
    ExtractFocalPoints,
    Filling {
        color: Color,
        fill_transparent: bool,
    },
    Focal(Rect),
    Format(Format),
    Grayscale,
    MaxBytes(u32),
    NoUpscale,
    Noise(u8),
    Proportion(f32),
    Quality(u8),
    RedEye,
    Rgb {
        r_amount: i8,
        g_amount: i8,
        b_amount: i8,
    },
    Rotate(u16),
    RoundCorners {
        radius: Radius,
        color: Color,
        transparent: bool,
    },
    Saturation(i8),
    Sharpen {
        sharpen_amount: f32,
        sharpen_radius: f32,
        luminance_only: bool,
    },
    Stretch,
    StripEXIF,
    StripICC,
    Upscale,
    Watermark {
        image_url: String,
        x: i32,
        y: i32,
        alpha: u8,
        w_ratio: Option<u8>,
        h_ratio: Option<u8>,
    },
    Custom {
        name: String,
        args: Vec<String>,
    },
}

impl Filter {
    fn args(&self) -> Vec<String> {
        match self {
            Filter::AutoJPG
            | Filter::Cover
            | Filter::Equalize
            | Filter::ExtractFocalPoints
            | Filter::Grayscale
            | Filter::NoUpscale
            | Filter::RedEye
            | Filter::Stretch
            | Filter::StripEXIF
            | Filter::StripICC
            | Filter::Upscale => vec![],
            Filter::Custom { args, .. } => args.clone(),
            Filter::BackgroundColor(color) => vec![color.to_string()],
            Filter::Brightness(brightness) => vec![brightness.to_string()],
            Filter::Contrast(contrast) => vec![contrast.to_string()],
            Filter::Focal(focal) => vec![focal.to_string()],
            Filter::Format(format) => vec![format.as_ref().to_string()],
            Filter::MaxBytes(n) => vec![n.to_string()],
            Filter::Noise(noise) => vec![noise.to_string()],
            Filter::Proportion(proportion) => vec![proportion.to_string()],
            Filter::Quality(quality) => vec![quality.to_string()],
            Filter::Rotate(rotate) => vec![rotate.to_string()],
            Filter::Saturation(saturation) => vec![saturation.to_string()],
            Filter::Blur { radius, sigma } => {
                let mut args = vec![radius.to_string()];
                if let Some(sigma) = sigma {
                    args.push(sigma.to_string());
                }
                args
            }
            Filter::Convolution {
                matrix_items,
                number_of_columns,
                should_normalize,
            } => vec![
                matrix_items
                    .iter()
                    .map(ToString::to_string)
                    .collect::<Vec<_>>()
                    .join(";"),
                number_of_columns.to_string(),
                should_normalize.to_string(),
            ],
            Filter::Filling {
                color,
                fill_transparent,
            } => {
                let mut args = vec![color.to_string()];
                if *fill_transparent {
                    args.push("1".to_string());
                }
                args
            }
            Filter::Rgb {
                r_amount,
                g_amount,
                b_amount,
            } => vec![
                r_amount.to_string(),
                g_amount.to_string(),
                b_amount.to_string(),
            ],
            Filter::RoundCorners {
                radius,
                color,
                transparent,
            } => {
                let mut args = vec![radius.to_string(), color.to_string()];
                if *transparent {
                    args.push("1".to_string());
                }
                args
            }
            Filter::Sharpen {
                sharpen_amount,
                sharpen_radius,
                luminance_only,
            } => vec![
                sharpen_amount.to_string(),
                sharpen_radius.to_string(),
                luminance_only.to_string(),
            ],
            Filter::Watermark {
                image_url,
                x,
                y,
                alpha,
                w_ratio,
                h_ratio,
            } => {
                let mut args = vec![
                    image_url.to_string(),
                    x.to_string(),
                    y.to_string(),
                    alpha.to_string(),
                ];
                if let Some(w_ratio) = w_ratio {
                    args.push(w_ratio.to_string());
                }
                if let Some(h_ratio) = h_ratio {
                    args.push(h_ratio.to_string());
                }
                args
            }
        }
    }
}

impl fmt::Display for Filter {
    #[allow(clippy::too_many_lines)]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let name = match self {
            Filter::Custom { name, .. } => name,
            _ => self.as_ref(),
        };

        write!(f, "{name}({})", self.args().join(","))
    }
}