rust-auth-utils 1.0.0

A rust port of @better-auth/utils.
Documentation
// based on https://github.com/better-auth/utils/blob/main/src/hash.ts

use crate::base64::{Base64, Base64Url};
use crate::types::{EncodingFormat, SHAFamily};
use sha1::Sha1;
use sha2::{Digest, Sha256, Sha384, Sha512};

pub struct Hash {
    algorithm: SHAFamily,
    encoding: Option<EncodingFormat>,
}

impl Hash {
    pub fn new(algorithm: SHAFamily, encoding: Option<EncodingFormat>) -> Self {
        Self {
            algorithm,
            encoding,
        }
    }

    pub async fn digest(
        &self,
        input: impl AsRef<[u8]>,
    ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
        let data = input.as_ref();

        let hash = match self.algorithm {
            SHAFamily::SHA1 => {
                let mut hasher = Sha1::new();
                hasher.update(data);
                hasher.finalize().to_vec()
            }
            SHAFamily::SHA256 => {
                let mut hasher = Sha256::new();
                hasher.update(data);
                hasher.finalize().to_vec()
            }
            SHAFamily::SHA384 => {
                let mut hasher = Sha384::new();
                hasher.update(data);
                hasher.finalize().to_vec()
            }
            SHAFamily::SHA512 => {
                let mut hasher = Sha512::new();
                hasher.update(data);
                hasher.finalize().to_vec()
            }
        };

        match self.encoding {
            Some(EncodingFormat::Hex) => Ok(hash
                .iter()
                .map(|b| format!("{:02x}", b))
                .collect::<String>()
                .into_bytes()),
            Some(EncodingFormat::Base64) => Ok(Base64::encode(&hash, None).into_bytes()),
            Some(EncodingFormat::Base64Url) => {
                Ok(Base64Url::encode(&hash, Some(true)).into_bytes())
            }
            Some(EncodingFormat::Base64UrlNoPad) => {
                Ok(Base64Url::encode(&hash, Some(false)).into_bytes())
            }
            Some(EncodingFormat::None) | None => Ok(hash),
        }
    }

    pub async fn digest_string(
        &self,
        input: impl AsRef<str>,
    ) -> Result<String, Box<dyn std::error::Error>> {
        let bytes = self.digest(input.as_ref().as_bytes()).await?;
        Ok(String::from_utf8(bytes)?)
    }
}

pub fn create_hash(algorithm: SHAFamily, encoding: Option<EncodingFormat>) -> Hash {
    Hash::new(algorithm, encoding)
}