From 0c2eaf0f1b5aa479b8ce296e08d2e0b4e8435fc0 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Thu, 12 Jan 2023 08:11:51 +0900 Subject: [PATCH] Define Fqn struct --- plume-models/src/lib.rs | 78 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/plume-models/src/lib.rs b/plume-models/src/lib.rs index e74a201c..335622f7 100644 --- a/plume-models/src/lib.rs +++ b/plume-models/src/lib.rs @@ -17,12 +17,16 @@ extern crate serde_json; extern crate tantivy; use activitystreams::iri_string; +use diesel::backend::Backend; +use diesel::sql_types::Text; +use diesel::types::{FromSql, ToSql}; pub use lettre; pub use lettre::smtp; use once_cell::sync::Lazy; -use plume_common::activity_pub::{inbox::InboxError, request, sign}; +use plume_common::activity_pub::{inbox::InboxError, request, sign, PreferredUsername}; use posts::PostEvent; use riker::actors::{channel, ActorSystem, ChannelRef, SystemBuilder}; +use std::{fmt, io::Write, string::ToString}; use users::UserEvent; #[cfg(not(any(feature = "sqlite", feature = "postgres")))] @@ -334,6 +338,78 @@ impl SmtpNewWithAddr for smtp::SmtpClient { } } +#[derive(AsExpression, PartialEq, Eq, Clone, FromSqlRow, Debug)] +#[sql_type = "Text"] +pub enum Fqn { + Local(PreferredUsername), + Remote(PreferredUsername, String), +} + +impl Fqn { + pub fn new_local(username: String) -> Result { + Ok(Self::Local( + PreferredUsername::new(username).map_err(|_| Error::InvalidValue)?, + )) + } + + pub fn new_remote(username: String, domain: String) -> Result { + Ok(Self::Remote( + PreferredUsername::new(username).map_err(|_| Error::InvalidValue)?, + domain, + )) + } +} + +impl From<&Fqn> for String { + fn from(fqn: &Fqn) -> Self { + match fqn { + Fqn::Local(username) => username.to_string(), + Fqn::Remote(username, domain) => format!("{}@{}", username, domain), + } + } +} + +impl fmt::Display for Fqn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + String::from(self).fmt(f) + } +} + +impl ToSql for Fqn +where + DB: diesel::backend::Backend, +{ + fn to_sql( + &self, + out: &mut diesel::serialize::Output, + ) -> diesel::serialize::Result { + let fqn = match self { + Self::Local(username) => username.to_string(), + Self::Remote(username, domain) => format!("{}@{}", username, domain), + }; + ToSql::::to_sql::(&fqn, out) + } +} + +impl FromSql for Fqn +where + DB: diesel::backend::Backend, + String: FromSql, +{ + /// We use PreferredUsername::new_unchecked() because, even if bytes is invalid as `preferredUsername`, + /// we need return some value. + fn from_sql(bytes: Option<&::RawValue>) -> diesel::deserialize::Result { + let value = >::from_sql(bytes)?; + Ok(match value.rsplit_once('@') { + None => Self::Local(unsafe { PreferredUsername::new_unchecked(value) }), + Some((username, domain)) => Self::Remote( + unsafe { PreferredUsername::new_unchecked(username.into()) }, + domain.into(), + ), + }) + } +} + #[cfg(test)] #[macro_use] mod tests {