use std::fmt;
use std::str::FromStr;
use crate::dat_key::Kid;
use crate::error::DatError;
use crate::util::now_unix_timestamp;
pub struct Dat<T: Kid> {
dat: String,
expire: u64,
kid: T,
plain_ptr: usize,
secure_ptr: usize,
signature_ptr: usize,
}
impl <T: Kid> Dat<T> {
pub fn kid(&self) -> &T {
&self.kid
}
pub fn expire(&self) -> u64 {
self.expire
}
pub fn plain_base64(&self) -> &str {
&self.dat[self.plain_ptr .. self.secure_ptr - 1]
}
pub fn secure_base64(&self) -> &str {
&self.dat[self.secure_ptr .. self.signature_ptr - 1]
}
pub fn sign_base64(&self) -> &str {
&self.dat[self.signature_ptr..]
}
pub fn body_str(&self) -> &str {
&self.dat[0 .. self.signature_ptr - 1]
}
}
impl <T: Kid + Clone> Clone for Dat<T> {
fn clone(&self) -> Self {
Dat {
dat: self.dat.clone(),
expire: self.expire,
kid: self.kid.clone(),
plain_ptr: self.plain_ptr,
secure_ptr: self.secure_ptr,
signature_ptr: self.signature_ptr,
}
}
}
impl <T: Kid> fmt::Display for Dat<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.dat)
}
}
impl <T: Kid> FromStr for Dat<T> {
type Err = DatError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.to_string().try_into()
}
}
impl <T: Kid> TryFrom<String> for Dat<T> {
type Error = DatError;
fn try_from(dat: String) -> Result<Self, Self::Error> {
let ptr = dat.as_ptr() as usize;
let mut parts = dat.split('.');
let expire = parts.next()
.and_then(|s| s.parse::<u64>().ok())
.filter(|e| *e > now_unix_timestamp())
.ok_or_else(|| DatError::InvalidDat)?;
let kid = parts.next().ok_or_else(|| DatError::InvalidDat)?;
let kid = T::from_str(kid).map_err(|_| DatError::InvalidDat)?;
let plain = parts.next().ok_or_else(|| DatError::InvalidDat)?;
let plain_ptr = plain.as_ptr() as usize - ptr;
let secure = parts.next().ok_or_else(|| DatError::InvalidDat)?;
let secure_ptr = secure.as_ptr() as usize - ptr;
let signature = parts.next().filter(|e| !e.is_empty()).ok_or_else(|| DatError::InvalidDat)?;
let signature_ptr = signature.as_ptr() as usize - ptr;
if parts.next().is_some() {
return Err(DatError::InvalidDat);
}
Ok(Dat {
dat,
expire,
kid,
plain_ptr,
secure_ptr,
signature_ptr,
})
}
}