use sha2::{Digest, Sha256};
use std::fmt;
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct SessionToken([u8; 32]);
impl SessionToken {
pub fn generate() -> Self {
let mut bytes = [0u8; 32];
rand::fill(&mut bytes);
Self(bytes)
}
pub fn from_hex(s: &str) -> Result<Self, &'static str> {
if s.len() != 64 {
return Err("token must be 64 hex characters");
}
let mut bytes = [0u8; 32];
for (i, chunk) in s.as_bytes().chunks(2).enumerate() {
let hi = hex_digit(chunk[0]).ok_or("invalid hex character")?;
let lo = hex_digit(chunk[1]).ok_or("invalid hex character")?;
bytes[i] = (hi << 4) | lo;
}
Ok(Self(bytes))
}
pub fn as_hex(&self) -> String {
crate::encoding::hex::encode(&self.0)
}
pub fn hash(&self) -> String {
let digest = Sha256::digest(self.0);
crate::encoding::hex::encode(&digest)
}
pub fn expose(&self) -> String {
self.as_hex()
}
pub fn from_raw(s: &str) -> Option<Self> {
Self::from_hex(s).ok()
}
}
impl fmt::Debug for SessionToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SessionToken(****)")
}
}
impl fmt::Display for SessionToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("****")
}
}
fn hex_digit(b: u8) -> Option<u8> {
match b {
b'0'..=b'9' => Some(b - b'0'),
b'a'..=b'f' => Some(b - b'a' + 10),
b'A'..=b'F' => Some(b - b'A' + 10),
_ => None,
}
}