use crate::{
fmt::impl_redacted_fmt,
random,
token::{Result, TokenMaterial},
};
#[derive(zeroize::ZeroizeOnDrop, Clone)]
pub struct GenericToken(Vec<u8>);
impl GenericToken {
pub fn random(len: usize) -> Result<Self> {
let mut buf = vec![0u8; len];
random::fill_bytes(&mut buf)?;
Ok(Self(buf))
}
#[must_use]
pub fn from_bytes(data: &[u8]) -> Self {
Self(data.to_vec())
}
}
impl TokenMaterial for GenericToken {
fn as_bytes(&self) -> &[u8] {
&self.0
}
fn from_vec(bytes: Vec<u8>) -> Result<Self> {
Ok(Self(bytes))
}
}
impl_redacted_fmt!(GenericToken);
#[cfg(feature = "serde")]
impl serde::Serialize for GenericToken {
fn serialize<S>(&self, serializer: S) -> std::prelude::v1::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
serializer.serialize_str(&self.to_base64())
} else {
serializer.serialize_bytes(self.as_bytes())
}
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for GenericToken {
fn deserialize<D>(deserializer: D) -> std::prelude::v1::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
if deserializer.is_human_readable() {
let s = String::deserialize(deserializer)?;
Self::from_base64(&s).map_err(serde::de::Error::custom)
} else {
let bytes = <Vec<u8>>::deserialize(deserializer)?;
Self::from_vec(bytes).map_err(serde::de::Error::custom)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn redacted_debug() {
let token = GenericToken::from_bytes(b"abc");
assert_eq!(format!("{:?}", token), "<redacted>");
}
#[test]
fn redacted_display() {
let token = GenericToken::from_bytes(b"abc");
assert_eq!(format!("{token}"), "<redacted>");
}
#[test]
#[cfg(feature = "serde")]
fn serde_human_readable_roundtrip() {
let token = GenericToken::from_bytes(b"abcdef");
let json = serde_json::to_string(&token).unwrap();
let deserialized = serde_json::from_str::<GenericToken>(&json).unwrap();
assert_eq!(token.0, deserialized.0);
assert_eq!(json, "\"YWJjZGVm\"");
}
}