mirror of
https://git.asonix.dog/asonix/pict-rs
synced 2025-01-11 03:55:50 +00:00
164 lines
3.9 KiB
Rust
164 lines
3.9 KiB
Rust
use diesel::{backend::Backend, sql_types::VarChar, AsExpression, FromSqlRow};
|
|
use uuid::Uuid;
|
|
|
|
use super::MaybeUuid;
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, AsExpression, FromSqlRow)]
|
|
#[diesel(sql_type = VarChar)]
|
|
pub(crate) struct DeleteToken {
|
|
id: MaybeUuid,
|
|
}
|
|
|
|
impl diesel::serialize::ToSql<VarChar, diesel::pg::Pg> for DeleteToken {
|
|
fn to_sql<'b>(
|
|
&'b self,
|
|
out: &mut diesel::serialize::Output<'b, '_, diesel::pg::Pg>,
|
|
) -> diesel::serialize::Result {
|
|
let s = self.to_string();
|
|
|
|
<String as diesel::serialize::ToSql<VarChar, diesel::pg::Pg>>::to_sql(
|
|
&s,
|
|
&mut out.reborrow(),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<B> diesel::deserialize::FromSql<VarChar, B> for DeleteToken
|
|
where
|
|
B: Backend,
|
|
String: diesel::deserialize::FromSql<VarChar, B>,
|
|
{
|
|
fn from_sql(
|
|
bytes: <B as diesel::backend::Backend>::RawValue<'_>,
|
|
) -> diesel::deserialize::Result<Self> {
|
|
let s = String::from_sql(bytes)?;
|
|
|
|
s.parse().map_err(From::from)
|
|
}
|
|
}
|
|
|
|
impl DeleteToken {
|
|
pub(crate) fn from_existing(existing: &str) -> Self {
|
|
if let Ok(uuid) = Uuid::parse_str(existing) {
|
|
DeleteToken {
|
|
id: MaybeUuid::Uuid(uuid),
|
|
}
|
|
} else {
|
|
DeleteToken {
|
|
id: MaybeUuid::Name(existing.into()),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn generate() -> Self {
|
|
Self {
|
|
id: MaybeUuid::Uuid(Uuid::new_v4()),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
|
self.id.as_bytes().to_vec()
|
|
}
|
|
|
|
pub(crate) fn from_slice(bytes: &[u8]) -> Option<Self> {
|
|
if let Ok(s) = std::str::from_utf8(bytes) {
|
|
Some(DeleteToken::from_existing(s))
|
|
} else if bytes.len() == 16 {
|
|
Some(DeleteToken {
|
|
id: MaybeUuid::Uuid(Uuid::from_slice(bytes).ok()?),
|
|
})
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub(crate) fn ct_eq(&self, rhs: &Self) -> bool {
|
|
subtle::ConstantTimeEq::ct_eq(self.id.as_bytes(), rhs.id.as_bytes()).unwrap_u8() == 1
|
|
}
|
|
}
|
|
|
|
impl std::str::FromStr for DeleteToken {
|
|
type Err = std::convert::Infallible;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
Ok(DeleteToken::from_existing(s))
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for DeleteToken {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}", self.id)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::{DeleteToken, MaybeUuid};
|
|
use uuid::Uuid;
|
|
|
|
#[test]
|
|
fn string_delete_token() {
|
|
let delete_token = DeleteToken::from_existing("blah");
|
|
|
|
assert_eq!(
|
|
delete_token,
|
|
DeleteToken {
|
|
id: MaybeUuid::Name(String::from("blah"))
|
|
}
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn uuid_string_delete_token() {
|
|
let uuid = Uuid::new_v4();
|
|
|
|
let delete_token = DeleteToken::from_existing(&uuid.to_string());
|
|
|
|
assert_eq!(
|
|
delete_token,
|
|
DeleteToken {
|
|
id: MaybeUuid::Uuid(uuid),
|
|
}
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn bytes_delete_token() {
|
|
let delete_token = DeleteToken::from_slice(b"blah").unwrap();
|
|
|
|
assert_eq!(
|
|
delete_token,
|
|
DeleteToken {
|
|
id: MaybeUuid::Name(String::from("blah"))
|
|
}
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn uuid_bytes_delete_token() {
|
|
let uuid = Uuid::new_v4();
|
|
|
|
let delete_token = DeleteToken::from_slice(&uuid.as_bytes()[..]).unwrap();
|
|
|
|
assert_eq!(
|
|
delete_token,
|
|
DeleteToken {
|
|
id: MaybeUuid::Uuid(uuid),
|
|
}
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn uuid_bytes_string_delete_token() {
|
|
let uuid = Uuid::new_v4();
|
|
|
|
let delete_token = DeleteToken::from_slice(uuid.to_string().as_bytes()).unwrap();
|
|
|
|
assert_eq!(
|
|
delete_token,
|
|
DeleteToken {
|
|
id: MaybeUuid::Uuid(uuid),
|
|
}
|
|
)
|
|
}
|
|
}
|