broot 1.56.0

File browser and launcher
Documentation
use {
    super::{
        svg,
        zune_compat::DynamicImage,
    },
    crate::errors::ProgramError,
    std::path::Path,
    termimad::{
        coolor,
        crossterm::style::Color as CrosstermColor,
    },
};

#[allow(clippy::large_enum_variant)]
pub enum SourceImage {
    Bitmap(DynamicImage),
    Svg(resvg::usvg::Tree),
}

impl SourceImage {
    pub fn new(path: &Path) -> Result<Self, ProgramError> {
        let is_svg = matches!(path.extension(), Some(ext) if ext == "svg" || ext == "SVG");
        let img = if is_svg {
            Self::Svg(svg::load(path)?)
        } else {
            Self::Bitmap(DynamicImage::from_path(path)?)
        };
        Ok(img)
    }
    pub fn dimensions(&self) -> (u32, u32) {
        match self {
            Self::Bitmap(img) => img.dimensions(),
            Self::Svg(tree) => (
                f32_to_u32(tree.size().width()),
                f32_to_u32(tree.size().height()),
            ),
        }
    }
    pub fn fitting(
        &self,
        mut max_width: u32,
        mut max_height: u32,
        bg_color: Option<CrosstermColor>,
    ) -> Result<DynamicImage, ProgramError> {
        let img = match self {
            Self::Bitmap(img) => {
                let dim = self.dimensions();
                if dim.0 <= max_width && dim.1 <= max_height {
                    img.clone()
                } else {
                    max_width = max_width.min(dim.0);
                    max_height = max_height.min(dim.1);
                    img.resize(max_width, max_height)?
                }
            }
            Self::Svg(tree) => {
                let bg_color: Option<coolor::Color> = bg_color.map(Into::into);
                svg::render_tree(tree, max_width, max_height, bg_color)?
            }
        };
        Ok(img)
    }
}

fn f32_to_u32(v: f32) -> u32 {
    if v <= 0.0 || v >= u32::MAX as f32 {
        0
    } else {
        v as u32
    }
}