dat 1.5.0

DAT - Distributed Access Token
Documentation
use crate::dat_kid::Kid;
use crate::error::DatError;
use crate::util::{decode_base64_url, now_unix_timestamp};
use std::fmt;
use std::str::FromStr;

pub struct Dat<T: Kid> {
    dat: Vec<u8>,
    expire: u64,
    kid: T,
    plain_pos: usize,
    secure_pos: usize,
    pub(crate) signature: Vec<u8>,
}
impl <T: Kid> Dat<T> {
    pub fn kid(&self) -> &T {
        &self.kid
    }
    pub fn expire(&self) -> u64 {
        self.expire
    }
    pub fn plain(&self) -> Result<Vec<u8>, DatError> {
        decode_base64_url(&self.dat[self.plain_pos.. self.secure_pos - 1])
    }
    pub fn secure(&self) -> Result<Vec<u8>, DatError> {
        decode_base64_url(&self.dat[self.secure_pos.. ])
    }
    pub fn body_bytes(&self) -> &[u8] {
        &self.dat[0..]
    }
}

impl <T: Kid> fmt::Display for Dat<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}.{}", self.expire.to_kid_string(), self.kid())
    }
}

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_pos = plain.as_ptr() as usize - ptr;

        let secure = parts.next().ok_or_else(|| DatError::InvalidDat)?;
        let secure_pos = secure.as_ptr() as usize - ptr;
        let secure_end = secure_pos + secure.len();

        let signature = parts.next().filter(|e| !e.is_empty()).ok_or_else(|| DatError::InvalidDat)?;

        if parts.next().is_some() {
            return Err(DatError::InvalidDat);
        }

        let signature = decode_base64_url(signature)?;
        let mut dat = dat.into_bytes();
        unsafe { dat.set_len(secure_end) };

        Ok(Dat {
            dat,
            expire,
            kid,
            plain_pos,
            secure_pos,
            signature,
        })
    }
}