use std::collections::HashMap;
use std::fmt::{self, Display, Formatter};
use std::ops::Add;
use std::str::FromStr;
use std::time::SystemTime;
use base64::Engine;
use base64::engine::general_purpose::STANDARD as b64;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_with::serde_as;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Base64Bytes(pub Vec<u8>);
impl Base64Bytes {
pub fn decode(&self) -> Result<Vec<u8>, base64::DecodeError> {
b64.decode(&self.0)
}
pub fn encode<T: AsRef<[u8]>>(input: T) -> Self {
let encoded = b64.encode(input.as_ref());
Self(encoded.into_bytes())
}
}
impl AsRef<[u8]> for Base64Bytes {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl From<&[u8]> for Base64Bytes {
fn from(slice: &[u8]) -> Self {
Self(slice.to_vec())
}
}
impl Display for Base64Bytes {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", String::from_utf8_lossy(&self.0))
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq)]
pub struct UnixTimestamp(u64);
impl Serialize for UnixTimestamp {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.0.to_string())
}
}
impl<'de> Deserialize<'de> for UnixTimestamp {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let ts = s
.parse::<u64>()
.map_err(|_| serde::de::Error::custom("timestamp must be a non-negative integer"))?;
Ok(Self(ts))
}
}
impl Display for UnixTimestamp {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Add<u64> for UnixTimestamp {
type Output = Self;
fn add(self, rhs: u64) -> Self::Output {
Self(self.0 + rhs)
}
}
impl UnixTimestamp {
#[must_use]
pub const fn from_secs(secs: u64) -> Self {
Self(secs)
}
#[must_use]
pub fn now() -> Self {
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
Self(now)
}
#[must_use]
pub const fn as_secs(self) -> u64 {
self.0
}
}
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Hash)]
pub struct Version<const N: u8>;
impl<const N: u8> Version<N> {
pub const VALUE: u8 = N;
}
impl<const N: u8> PartialEq<u8> for Version<N> {
fn eq(&self, other: &u8) -> bool {
*other == N
}
}
impl<const N: u8> From<Version<N>> for u8 {
fn from(_: Version<N>) -> Self {
N
}
}
impl<const N: u8> Display for Version<N> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{N}")
}
}
impl<const N: u8> Serialize for Version<N> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_u8(N)
}
}
impl<'de, const N: u8> Deserialize<'de> for Version<N> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let v = u8::deserialize(deserializer)?;
if v == N {
Ok(Self)
} else {
Err(serde::de::Error::custom(format!(
"expected version {N}, got {v}"
)))
}
}
}
pub type Extensions = HashMap<String, serde_json::Value>;
#[serde_as]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct U64String(#[serde_as(as = "serde_with::DisplayFromStr")] u64);
impl U64String {
#[must_use]
pub const fn inner(self) -> u64 {
self.0
}
}
impl FromStr for U64String {
type Err = <u64 as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse::<u64>().map(Self)
}
}
impl From<u64> for U64String {
fn from(value: u64) -> Self {
Self(value)
}
}
impl From<U64String> for u64 {
fn from(value: U64String) -> Self {
value.0
}
}