make_it_braille 0.6.0

A CLI tool and library to manipulate unicode braille images
Documentation
use std::io::Cursor;

use image::{AnimationDecoder, ImageFormat};
use log::debug;
use reqwest::{header::HeaderMap, Url};
use thiserror::Error;

#[derive(Debug, Error)]
pub enum Error {
    #[error("the image doesn't have a frame of number {0}")]
    NoSuchFrame(u32),
    #[error(transparent)]
    InvalidImage(#[from] image::error::ImageError),
    #[error("there as an I/O error!")]
    Io(#[from] std::io::Error),
    #[error(transparent)]
    Fetch(#[from] FetchError)
}

#[derive(Debug, Error)]
pub enum FetchError {
    #[error("the provided URL was not valid")]
    BadUrl,
    #[error(transparent)]
    RequestError(#[from] reqwest::Error),
    #[error("the server's response was bad")]
    BadResponse,
    #[error(transparent)]
    BadImageData(#[from] image::error::ImageError)
}

pub fn try_get_from_url(url: Url) -> Result<image::Frames<'static>, Error> {
    let mut headers = HeaderMap::new();
    headers.insert("Accept", "image/png,image/jpeg,image/webp,image/gif,image/tiff".parse().unwrap());
    headers.insert("Referer", url.host_str().ok_or(FetchError::BadUrl)?.parse().unwrap());
    headers.insert("Cache-Control", "no-cache".parse().unwrap());
    headers.insert("User-Agent", format!("{}/{}", env!("CARGO_BIN_NAME"), env!("CARGO_PKG_VERSION")).parse().unwrap());


    let client = reqwest::blocking::ClientBuilder::new()
        .default_headers(headers)
        .build().unwrap();

    let resp = client.get(url)
        .send()
        .map_err(FetchError::RequestError)?
        .error_for_status()
        .map_err(FetchError::RequestError)?;

    debug!("response info: {:#?}", &resp);

    let format_header = resp.headers().get(reqwest::header::CONTENT_TYPE).cloned();
    let payload = resp.bytes().map_err(|_| { FetchError::BadResponse })?;

    let format = format_header.and_then(|h| {
        image::ImageFormat::from_mime_type(h.to_str().ok()?)
    });

    load_as_frames(payload.clone(), format)
}

pub fn load_as_frames(data: impl AsRef<[u8]> + 'static, format: Option<ImageFormat>) -> Result<image::Frames<'static>, Error> {
    match format {
        Some(ImageFormat::Gif) => {
            let decoder = image::codecs::gif::GifDecoder::new(Cursor::new(data))?;
            Ok(decoder.into_frames())
        },
        Some(ImageFormat::WebP) => {
            let decoder = image::codecs::webp::WebPDecoder::new(Cursor::new(data))?;
            Ok(decoder.into_frames())
        }
        Some(f) => {
            let frame = image::load_from_memory_with_format(data.as_ref(), f)?;
            Ok(image::Frames::new(Box::new([Ok(image::Frame::new(frame.into_rgba8()))].into_iter())))
        }
        None => {
            let guessed = image::guess_format(data.as_ref())?;
            load_as_frames(data, Some(guessed))
        }
    }
}