#[cfg(feature = "postgres")] use diesel::{backend::Backend, sql_types::VarChar, AsExpression, FromSqlRow}; use uuid::Uuid; use super::MaybeUuid; #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "postgres", derive(AsExpression, FromSqlRow))] #[cfg_attr(feature = "postgres", diesel(sql_type = VarChar))] pub(crate) struct DeleteToken { id: MaybeUuid, } #[cfg(feature = "postgres")] impl diesel::serialize::ToSql 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(); >::to_sql( &s, &mut out.reborrow(), ) } } #[cfg(feature = "postgres")] impl diesel::deserialize::FromSql for DeleteToken where B: Backend, String: diesel::deserialize::FromSql, { fn from_sql( bytes: ::RawValue<'_>, ) -> diesel::deserialize::Result { 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()), } } #[cfg(feature = "sled")] pub(crate) fn to_bytes(&self) -> Vec { self.id.as_bytes().to_vec() } #[cfg(feature = "sled")] pub(crate) fn from_slice(bytes: &[u8]) -> Option { 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 } } } impl std::str::FromStr for DeleteToken { type Err = std::convert::Infallible; fn from_str(s: &str) -> Result { 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), } ) } }