okid 0.23.0

A library for generating double clickable ids
use crate::binary_type::BinaryType;
use crate::digest::Digest;
use crate::error::Error;
use crate::{fingerprint, pub_key, OkId, SEPARATOR, SEPARATOR_BYTES, SEPARATOR_BYTES_LEN};
#[cfg(feature = "blake3")]
use crate::blake3;
#[cfg(feature = "sha1")]
use crate::sha1;
#[cfg(feature = "sha2")]
use crate::sha2;
#[cfg(feature = "sha3")]
use crate::sha3;
#[cfg(feature = "ulid")]
use crate::ulid;
#[cfg(feature = "uuid")]
use crate::uuid;

pub(crate) fn parse_okid(s: &str) -> Result<OkId, Error> {
    let mut chars = s.chars();
    let hash_type: BinaryType = chars.next().ok_or(Error::InvalidFormat)?.into();
    if chars.next() != Some(SEPARATOR) {
        return Err(Error::InvalidFormat);
    }
    let rest = chars.collect::<String>();
    match hash_type {
        #[cfg(feature = "sha1")]
        BinaryType::Sha1 => Ok(OkId {
            hash_type,
            digest: Digest::Sha1(rest.parse()?),
        }),
        #[cfg(feature = "sha2")]
        BinaryType::Sha256 => Ok(OkId {
            hash_type,
            digest: Digest::Sha256(rest.parse()?),
        }),
        #[cfg(feature = "sha3")]
        BinaryType::Sha3_512 => Ok(OkId {
            hash_type,
            digest: Digest::Sha512(rest.parse()?),
        }),
        #[cfg(feature = "blake3")]
        BinaryType::Blake3 => Ok(OkId {
            hash_type,
            digest: Digest::Blake3(rest.parse()?),
        }),
        #[cfg(feature = "ulid")]
        BinaryType::Ulid => Ok(OkId {
            hash_type,
            digest: Digest::Ulid(rest.parse()?),
        }),
        #[cfg(feature = "uuid")]
        BinaryType::Uuid => Ok(OkId {
            hash_type,
            digest: Digest::Uuid(rest.parse()?),
        }),
        BinaryType::Unknown => Err(Error::InvalidHashType),
        BinaryType::Fingerprint => Ok(OkId {
            hash_type,
            digest: Digest::Fingerprint(rest.parse()?),
        }),
        BinaryType::PubKey => Ok(OkId {
            hash_type,
            digest: Digest::PubKey(rest.parse()?),
        }),
    }
}

#[doc(hidden)]
pub const fn const_parse_okid(s: &str) -> Option<OkId> {
    if s.len() < 1 + SEPARATOR_BYTES_LEN {
        return None;
    }

    let bytes = s.as_bytes();

    parse_okid_bytes(bytes)
}

const fn parse_okid_bytes(bytes: &[u8]) -> Option<OkId> {
    let hash_type = match bytes[0] {
        #[cfg(feature = "sha1")]
        b'1' => BinaryType::Sha1,
        #[cfg(feature = "sha2")]
        b'2' => BinaryType::Sha256,
        #[cfg(feature = "sha3")]
        b'3' => BinaryType::Sha3_512,
        #[cfg(feature = "blake3")]
        b'b' => BinaryType::Blake3,
        #[cfg(feature = "ulid")]
        b'u' => BinaryType::Ulid,
        #[cfg(feature = "uuid")]
        b'i' => BinaryType::Uuid,
        b'f' => BinaryType::Fingerprint,
        b'p' => BinaryType::PubKey,
        _ => return None,
    };

    let mut i = 0;
    while i < SEPARATOR_BYTES_LEN {
        if bytes[1 + i] != SEPARATOR_BYTES[i] {
            return None;
        }
        i += 1;
    }

    let content_start = 1 + SEPARATOR_BYTES_LEN;

    match hash_type {
        #[allow(deprecated)]
        #[cfg(feature = "sha1")]
        BinaryType::Sha1 => {
            if bytes.len() != content_start + 40 {
                return None;
            }
            match sha1::parse_sha1_bytes(bytes, content_start) {
                Some(digest) => Some(OkId {
                    hash_type,
                    digest: Digest::Sha1(digest),
                }),
                None => None,
            }
        }
        #[cfg(feature = "sha2")]
        BinaryType::Sha256 => {
            if bytes.len() != content_start + 64 {
                return None;
            }
            match sha2::parse_sha256_bytes(bytes, content_start) {
                Some(digest) => Some(OkId {
                    hash_type,
                    digest: Digest::Sha256(digest),
                }),
                None => None,
            }
        }
        #[cfg(feature = "sha3")]
        BinaryType::Sha3_512 => {
            if bytes.len() != content_start + 128 {
                return None;
            }
            match sha3::parse_sha3_bytes(bytes, content_start) {
                Some(digest) => Some(OkId {
                    hash_type,
                    digest: Digest::Sha512(digest),
                }),
                None => None,
            }
        }
        #[cfg(feature = "blake3")]
        BinaryType::Blake3 => {
            if bytes.len() != content_start + 64 {
                return None;
            }
            match blake3::parse_blake3_bytes(bytes, content_start) {
                Some(digest) => Some(OkId {
                    hash_type,
                    digest: Digest::Blake3(digest),
                }),
                None => None,
            }
        }
        #[cfg(feature = "ulid")]
        BinaryType::Ulid => {
            if bytes.len() != content_start + 32 {
                return None;
            }
            match ulid::parse_ulid_bytes(bytes, content_start) {
                Some(digest) => Some(OkId {
                    hash_type,
                    digest: Digest::Ulid(digest),
                }),
                None => None,
            }
        }
        #[cfg(feature = "uuid")]
        BinaryType::Uuid => {
            if bytes.len() != content_start + 32 {
                return None;
            }
            match uuid::parse_uuid_bytes(bytes, content_start) {
                Some(digest) => Some(OkId {
                    hash_type,
                    digest: Digest::Uuid(digest),
                }),
                None => None,
            }
        }
        BinaryType::Fingerprint => {
            if bytes.len() != content_start + 16 {
                return None;
            }
            match fingerprint::parse_fingerprint_bytes(bytes, content_start) {
                Some(digest) => Some(OkId {
                    hash_type,
                    digest: Digest::Fingerprint(digest),
                }),
                None => None,
            }
        }
        BinaryType::PubKey => {
            if bytes.len() != content_start + 64 {
                return None;
            }
            match pub_key::parse_pub_key_bytes(bytes, content_start) {
                Some(digest) => Some(OkId {
                    hash_type,
                    digest: Digest::PubKey(digest),
                }),
                None => None,
            }
        }
        _ => None,
    }
}

#[inline]
pub(crate) const fn hex_to_byte(c: u8) -> Option<u8> {
    match c {
        b'0'..=b'9' => Some(c - b'0'),
        b'a'..=b'f' => Some(c - b'a' + 10),
        b'A'..=b'F' => Some(c - b'A' + 10),
        _ => None,
    }
}