libwebp2 0.0.2

libwebp bindings
Documentation
use std::{env, io::Read, path::PathBuf};

use anyhow::{bail, ensure, Result};
use flate2::read::GzDecoder;
use reqwest::{Client, IntoUrl};
use tar::Archive;

#[tokio::main]
async fn main() -> Result<()> {
    println!("cargo:rerun-if-changed=build.rs");
    if env::var("CARGO_FEATURE_DOWNLOAD").is_err() {
        return Ok(());
    }

    let root = prep_workdir().await?;

    let config = config()?;

    let targz = get(config.url()).await?;

    let tar = gz_unzip(&*targz)?;

    for entry in Archive::new(&*tar).entries()? {
        let mut entry = entry?;
        let path = entry.path()?.to_path_buf();
        for lib in config.libs() {
            if path.ends_with(&lib) {
                let libpath = root.join(&lib);
                entry.unpack(&libpath)?;
                println!("cargo:rustc-link-arg={}", libpath.display());
            }
        }
    }

    if env::var("CARGO_CFG_TARGET_OS")? == "linux" {
        println!("cargo:rustc-link-arg=-lm");
    }

    Ok(())
}

trait Config {
    fn url(&self) -> String;
    fn libs(&self) -> Vec<String>;
}

struct LinuxX64;

impl Config for LinuxX64 {
    fn url(&self) -> String {
        "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.3.2-linux-x86-64.tar.gz".to_string()
    }

    fn libs(&self) -> Vec<String> {
        vec!["libwebp.a".to_string(), "libsharpyuv.a".to_string()]
    }
}

struct LinuxAarch64;

impl Config for LinuxAarch64 {
    fn url(&self) -> String {
        "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.3.2-linux-aarch64.tar.gz".to_string()
    }

    fn libs(&self) -> Vec<String> {
        vec!["libwebp.a".to_string(), "libsharpyuv.a".to_string()]
    }
}

struct MacosX64;

impl Config for MacosX64 {
    fn url(&self) -> String {
        "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.3.2-mac-x86-64.tar.gz".to_string()
    }

    fn libs(&self) -> Vec<String> {
        vec!["libwebp.a".to_string(), "libsharpyuv.a".to_string()]
    }
}

struct MacosAarch64;

impl Config for MacosAarch64 {
    fn url(&self) -> String {
        "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.3.2-mac-arm64.tar.gz".to_string()
    }

    fn libs(&self) -> Vec<String> {
        vec!["libwebp.a".to_string(), "libsharpyuv.a".to_string()]
    }
}

fn config() -> Result<Box<dyn Config>> {
    let arch = env::var("CARGO_CFG_TARGET_ARCH")?;
    let os = env::var("CARGO_CFG_TARGET_OS")?;
    let config = match (&*os, &*arch) {
        ("linux", "x86_64") => Box::new(LinuxX64),
        _ => bail!("unsupported os or arch"),
    };
    Ok(config)
}

async fn get(url: impl IntoUrl) -> Result<Vec<u8>> {
    let response = Client::builder()
        .use_rustls_tls()
        .https_only(true)
        .build()?
        .get(url)
        .send()
        .await?;
    ensure!(response.status().is_success());
    Ok(response.bytes().await?.to_vec())
}

async fn prep_workdir() -> Result<PathBuf> {
    let root = PathBuf::from(env::var("OUT_DIR")?)
        .canonicalize()?
        .join("root");
    if root.exists() {
        tokio::fs::remove_dir_all(&root).await?;
    }
    tokio::fs::create_dir(&root).await?;
    Ok(root)
}

fn gz_unzip(r: impl Read) -> Result<Vec<u8>> {
    let mut out = Vec::new();
    GzDecoder::new(r).read_to_end(&mut out)?;
    Ok(out)
}