use crate::{Error, Unit};
use enum_as_inner::EnumAsInner;
use generic_array::{
typenum::consts::{U12, U16},
GenericArray,
};
use libipld::{multibase::Base::Base32HexLower, Ipld};
use serde::{Deserialize, Serialize};
use std::fmt;
use uuid::Uuid;
type Nonce96 = GenericArray<u8, U12>;
type Nonce128 = GenericArray<u8, U16>;
#[derive(Clone, Debug, PartialEq, EnumAsInner, Serialize, Deserialize)]
pub enum Nonce {
Nonce96(Nonce96),
Nonce128(Nonce128),
Empty,
}
impl Nonce {
pub fn generate() -> Self {
Nonce::Nonce96(*GenericArray::from_slice(xid::new().as_bytes()))
}
pub fn generate_128() -> Self {
Nonce::Nonce128(*GenericArray::from_slice(Uuid::new_v4().as_bytes()))
}
}
impl fmt::Display for Nonce {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Nonce::Nonce96(nonce) => {
write!(f, "{}", Base32HexLower.encode(nonce.as_slice()))
}
Nonce::Nonce128(nonce) => {
write!(f, "{}", Base32HexLower.encode(nonce.as_slice()))
}
Nonce::Empty => write!(f, ""),
}
}
}
impl From<Nonce> for Ipld {
fn from(nonce: Nonce) -> Self {
match nonce {
Nonce::Nonce96(nonce) => Ipld::Bytes(nonce.to_vec()),
Nonce::Nonce128(nonce) => Ipld::Bytes(nonce.to_vec()),
Nonce::Empty => Ipld::String("".to_string()),
}
}
}
impl TryFrom<Ipld> for Nonce {
type Error = Error<Unit>;
fn try_from(ipld: Ipld) -> Result<Self, Self::Error> {
if let Ipld::Bytes(v) = ipld {
match v.len() {
12 => Ok(Nonce::Nonce96(*GenericArray::from_slice(&v))),
16 => Ok(Nonce::Nonce128(*GenericArray::from_slice(&v))),
other_ipld => Err(Error::unexpected_ipld(other_ipld.to_owned().into())),
}
} else {
Ok(Nonce::Empty)
}
}
}
impl TryFrom<&Ipld> for Nonce {
type Error = Error<Unit>;
fn try_from(ipld: &Ipld) -> Result<Self, Self::Error> {
TryFrom::try_from(ipld.to_owned())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn ipld_roundtrip_12() {
let gen = Nonce::generate();
let ipld = Ipld::from(gen.clone());
let inner = if let Nonce::Nonce96(nonce) = gen {
Ipld::Bytes(nonce.to_vec())
} else {
panic!("No conversion!")
};
assert_eq!(ipld, inner);
assert_eq!(gen, ipld.try_into().unwrap());
}
#[test]
fn ipld_roundtrip_16() {
let gen = Nonce::generate_128();
let ipld = Ipld::from(gen.clone());
let inner = if let Nonce::Nonce128(nonce) = gen {
Ipld::Bytes(nonce.to_vec())
} else {
panic!("No conversion!")
};
assert_eq!(ipld, inner);
assert_eq!(gen, ipld.try_into().unwrap());
}
#[test]
fn ser_de() {
let gen = Nonce::generate_128();
let ser = serde_json::to_string(&gen).unwrap();
let de = serde_json::from_str(&ser).unwrap();
assert_eq!(gen, de);
}
}