dicebear 0.1.4

An unofficial dicebear wrapper for Rust
Documentation
//! A really simple dicebear wrapper for Rust
#![allow(unused)]
use reqwest;
use image::codecs::png;
use std::io::Cursor;

// For builder
mod builder;
pub use builder::DicebearBuilder;

pub mod exports {
    pub use image::{self, DynamicImage, GenericImageView, ImageReader};
}


/// Generates an URL linking to an avatar from `DicebearBuilder`
pub async fn generate_from_builder(builder:DicebearBuilder) -> Result<exports::DynamicImage, Box<dyn std::error::Error>> {
    return generate(builder.avatar_style, builder.seed, builder.size, builder.flip, builder.options).await;
}

/// Generates an avatar as image::DynamicImage from `DicebearBuilder`
pub async fn generate_url_from_builder(format:String, builder:DicebearBuilder) -> Result<String, Box<dyn std::error::Error>> {
    return generate_url(format, builder.avatar_style, builder.seed, builder.size, builder.flip, builder.options).await;
}


/// Generates an URL linking to an avatar and does nothing with it
pub async fn generate_url(
    format:         String,
    avatar_style:   String,
    seed:           Option<String>,
    size:           String,
    flip:           Option<bool>,
    options:        Option<Vec<String>>,
) -> Result<String, Box<dyn std::error::Error>> {
    let flip_str    = bool::to_string(&flip.unwrap_or(false));
    let url         = format!(
                        "https://api.dicebear.com/9.x/{}/{}?seed={}&size={}&flip={}&{}",
                        avatar_style,
                        format,
                        seed.unwrap_or(String::from("")),
                        size,
                        flip_str,
                        options
                            .map(|v| v.join("&"))
                            .unwrap_or_else(|| String::from("")),
                    );
    Ok(String::from(url))
}

/// Generates an avatar as a PNG image and returns it as an image::DynamicImage
pub async fn generate(
        avatar_style:   String,
        seed:           Option<String>,
        size:           String,
        flip:           Option<bool>,
        options:        Option<Vec<String>>
 ) -> Result<exports::DynamicImage, Box<dyn std::error::Error>> {
    let url         =   generate_url(
                            String::from("png"),
                            avatar_style,
                            seed,
                            size,
                            flip,
                            options,
                        ).await;
    let response    = reqwest::get(&url.unwrap())
                        .await;
    match response {
        Ok(png_img) => {
            if png_img.status().is_success() {
                let img = exports::image::ImageReader::new(Cursor::new(png_img.bytes().await?))
                    .with_guessed_format()?
                    .decode()?;

                Ok(img)
            } else {
                Err("could not fetch the image, mismatched style?".into())
            }
        }
        Err(e) => Err(Box::new(e)),
    }
}

#[cfg(test)]
mod tests {
    use image::GenericImageView;
    use super::*;
    use tokio;

    #[tokio::test]
    async fn get_bottts_image() {
        let result  = generate(
            String::from("bottts"),
            Some(String::from("dunno")),
            String::from("256"),
            Some(true),
            None,
        ).await;
        let img     = result.unwrap();
        assert!(img.dimensions().0 == 256, "bottts: width is wrong for unknown reasons! aborting!");
        assert!(img.dimensions().1 == 256, "bottts: height is wrong for unknown reasons! aborting!");
    }

    #[tokio::test]
    async fn get_thumbs_image() {
        let result  = generate(
            String::from("thumbs"),
            Some(String::from("teesh3rt")),
            String::from("128"),
            Some(false),
            None,
        ).await;
        let img     = result.unwrap();
        assert!(img.dimensions().0 == 128, "thumbs: width is wrong for unknown reasons! aborting!");
        assert!(img.dimensions().1 == 128, "thumbs: height is wrong for unknown reasons! aborting!");
    }

    #[tokio::test]
    async fn test_url() {
        let url = generate_url(
            String::from("png"),
            String::from("thumbs"),
            Some(String::from("user0")),
            String::from("64"),
            Some(false),
            None
        ).await.unwrap();
        assert!(
            url == "https://api.dicebear.com/9.x/thumbs/png?seed=user0&size=64&flip=false&",
            "url did not match. generated url: {url}"
        );
    }
}