use serde::de::Error; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; use std::fmt::Debug; use std::str::FromStr; pub mod acct; pub mod activity_streams; pub trait ContentType: Serialize { fn mime_type(&self) -> &'static str; } macro_rules! content_type { ($visib:vis $typ:ident, $expression:expr) => { #[derive(Copy, Clone, Eq, PartialEq, Debug, Default)] $visib struct $typ; impl AsRef for $typ { fn as_ref(&self) -> &'static str { $expression } } impl Serialize for $typ { fn serialize(&self, serializer: S) -> Result { serializer.serialize_str($expression) } } impl ContentType for $typ { fn mime_type(&self) -> &'static str { $expression } } impl<'de> Deserialize<'de> for $typ { fn deserialize>(deserializer: D) -> Result { let content_type = String::deserialize(deserializer)?; if matches!(content_type.as_ref(), $expression) { Ok(Self) } else { Err(Error::custom(format!( "Invalid content type: {content_type}" ))) } } } }; } pub mod content_type { use crate::web_model::ContentType; use serde::de::Error; use serde::{Deserialize, Deserializer, Serialize, Serializer}; content_type!(pub ContentActivityStreams, "application/activity+json"); content_type!(pub ContentHtml, "text/html"); content_type!(pub ContentJson, "application/json"); content_type!(pub ContentJrdJson, "application/jrd+json"); content_type!(pub ContentMultipartFormData, "multipart/form-data"); content_type!(pub ContentUrlEncoded, "application/x-www-form-urlencoded"); } macro_rules! link_rel { ($visib:vis $typ:ident, $expression:expr) => { #[derive(Copy, Clone, Eq, PartialEq, Debug, Default)] $visib struct $typ; impl AsRef for $typ { fn as_ref(&self) -> &'static str { $expression } } impl Serialize for $typ { fn serialize(&self, serializer: S) -> Result { serializer.serialize_str($expression) } } impl Rel for $typ { fn rel(&self) -> &'static str { $expression } } impl<'de> Deserialize<'de> for $typ { fn deserialize>(deserializer: D) -> Result { let rel = String::deserialize(deserializer)?; if matches!(rel.as_ref(), $expression) { Ok(Self) } else { Err(Error::custom(format!( "Invalid rel: {rel}" ))) } } } }; } pub trait Rel: Serialize { fn rel(&self) -> &'static str; } pub mod rel { use crate::web_model::Rel; use serde::de::Error; use serde::{Deserialize, Deserializer, Serialize, Serializer}; link_rel!(pub RelWebFingerProfilePage, "http://webfinger.net/rel/profile-page"); link_rel!(pub RelSelf, "self"); link_rel!(pub RelOStatusSubscribe, "http://ostatus.org/schema/1.0/subscribe"); link_rel!(pub RelNodeInfo20, "http://nodeinfo.diaspora.software/ns/schema/2.0"); link_rel!(pub RelNodeInfo21, "http://nodeinfo.diaspora.software/ns/schema/2.1"); } #[derive(Clone, Eq, PartialEq, Debug)] pub struct ListContaining + FromStr>(pub T); impl Serialize for ListContaining where T: Clone + Eq + PartialEq + Debug + AsRef + FromStr, { fn serialize(&self, serializer: S) -> Result { vec![AsRef::::as_ref(&self.0)].serialize(serializer) } } impl<'de, T> Deserialize<'de> for ListContaining where T: Clone + Eq + PartialEq + Debug + AsRef + FromStr, { fn deserialize>(deserializer: D) -> Result { let data = Vec::::deserialize(deserializer)?; let dt = data .iter() .filter_map(Value::as_str) .filter_map(|val| T::from_str(val).ok()) .next(); if let Some(value) = dt { Ok(ListContaining(value)) } else { Err(Error::custom("Count not find item in list.".to_string())) } } }