use std::{fmt, str};
use base64::engine::general_purpose::URL_SAFE as BASE64_URL_SAFE;
use base64::Engine as _;
use crate::model::id::UserId;
pub fn validate(token: impl AsRef<str>) -> Result<(), InvalidToken> {
parse(token).map(|_| ()).ok_or(InvalidToken)
}
#[derive(Debug)]
pub struct InvalidToken;
impl std::error::Error for InvalidToken {}
impl fmt::Display for InvalidToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("The provided token was invalid")
}
}
pub fn parse(token: impl AsRef<str>) -> Option<(UserId, i64)> {
let mut parts = token.as_ref().trim_start_matches("Bot ").split('.');
let user_id = BASE64_URL_SAFE.decode(parts.next()?).ok()?;
let user_id = UserId(str::from_utf8(&user_id).ok()?.parse().ok()?);
let timestamp = parts.next()?;
if timestamp.len() < 6 {
return None;
}
let timestamp_bytes = BASE64_URL_SAFE.decode(timestamp).ok()?;
let mut timestamp = 0;
for byte in timestamp_bytes {
timestamp *= 256;
timestamp += byte as i64;
}
if timestamp < 1_293_840_000 {
timestamp += 1_293_840_000;
}
BASE64_URL_SAFE.decode(parts.next()?).ok()?;
Some((user_id, timestamp))
}