bunny-api 0.0.5

Alpha API client for Bunny.net
Documentation
use std::{fmt, io};
use std::path::{Path, PathBuf};

use sha2::{Sha256, Digest};
use tokio::fs;
use tokio::io::AsyncReadExt;

#[derive(Debug, thiserror::Error)]
#[error("{kind} ({path}): {error}")]
pub struct Error {
    path: PathBuf,
    kind: Operation,
    #[source]
    error: io::Error,
}

impl Error {
    pub const fn new(path: PathBuf, kind: Operation, error: io::Error) -> Self {
        Self { path, kind, error }
    }
}

#[derive(Debug)]
pub enum Operation {
    Open,
    Read,
}

impl fmt::Display for Operation {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Open => write!(f, "failed to open file"),
            Self::Read => write!(f, "failed to read file"),
        }
    }
}

pub(crate) async fn file_content(file_path: &Path) -> Result<Vec<u8>, Error> {
    #[cfg(feature = "tracing")]
    tracing::debug!("reading contents of {}", file_path.display());

    let mut file = fs::File::open(file_path)
        .await
        .map_err(|error| Error::new(file_path.to_path_buf(), Operation::Open, error))?;
    let mut content = Vec::new();
    file.read_to_end(&mut content)
        .await
        .map_err(|error| Error::new(file_path.to_path_buf(), Operation::Read, error))?;
    Ok(content)
}

pub(crate) fn checksum(content: &[u8]) -> String {
    let mut hasher = Sha256::new();
    hasher.update(&content);
    format!("{:X}", hasher.finalize())
}

pub(crate) fn urlencode_path(path: &str) -> String {
    path.split_terminator('/')
        .map(|comp| urlencoding::encode(comp).to_string())
        .collect::<Vec<String>>()
        .join("/")
}