diff --git a/Cargo.toml b/Cargo.toml index 279d1f6b..3f02d12a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" axum = "0.7.3" chrono = { version = "0.4.31", features = ["serde"] } clap = { version = "4.5.3", features = ["derive"] } +reqwest = { version = "0.11.26", features = ["json"] } sea-orm = { version = "0.12.14", features = ["macros", "sqlx-sqlite", "runtime-tokio-rustls"] } sea-orm-migration = "0.12.15" serde = { version = "1.0.193", features = ["derive"] } diff --git a/src/activitystream/activity.rs b/src/activitystream/activity.rs deleted file mode 100644 index 004feba7..00000000 --- a/src/activitystream/activity.rs +++ /dev/null @@ -1,56 +0,0 @@ -pub trait Activity : super::Object { - fn activity_type(&self) -> Option { None } - - fn actor_id(&self) -> Option<&str> { None } - fn actor(&self) -> Option> { None::> } - - fn object_id(&self) -> Option<&str> { None } - fn object(&self) -> Option> { None::> } - - fn target(&self) -> Option<&str> { None } -} - -impl Activity for serde_json::Value { - fn activity_type(&self) -> Option { - let serde_json::Value::String(t) = self.get("type")? else { return None }; - super::types::ActivityType::try_from(t.as_str()).ok() - } - - fn object(&self) -> Option> { - let obj = self.get("object")?; - match obj { - serde_json::Value::Object(_) => Some(obj.clone().into()), - _ => None, - } - } - - fn object_id(&self) -> Option<&str> { - match self.get("object")? { - serde_json::Value::Object(map) => match map.get("id")? { - serde_json::Value::String(id) => Some(id), - _ => None, - }, - serde_json::Value::String(id) => Some(id), - _ => None, - } - } - - fn actor(&self) -> Option> { - let obj = self.get("actor")?; - match obj { - serde_json::Value::Object(_) => Some(obj.clone().into()), - _ => None, - } - } - - fn actor_id(&self) -> Option<&str> { - match self.get("actor")? { - serde_json::Value::Object(map) => match map.get("id")? { - serde_json::Value::String(id) => Some(id), - _ => None, - }, - serde_json::Value::String(id) => Some(id), - _ => None, - } - } -} diff --git a/src/activitystream/actor.rs b/src/activitystream/actor.rs deleted file mode 100644 index 70a2a75d..00000000 --- a/src/activitystream/actor.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub trait Actor : super::Object { - fn actor_type(&self) -> Option { None } -} diff --git a/src/activitystream/link.rs b/src/activitystream/link.rs index 4aa65b5c..90411181 100644 --- a/src/activitystream/link.rs +++ b/src/activitystream/link.rs @@ -1,4 +1,13 @@ -pub trait Link { +use crate::strenum; + +strenum! { + pub enum LinkType { + Link, + Mention + } +} + +pub trait Link : super::Base { fn href(&self) -> &str; fn rel(&self) -> Option<&str> { None } fn media_type(&self) -> Option<&str> { None } // also in obj @@ -9,27 +18,6 @@ pub trait Link { fn preview(&self) -> Option<&str> { None } // also in obj } -pub enum LinkedObject { - Object(T), - Link(Box), -} - -impl LinkedObject -where - T : for<'de> serde::Deserialize<'de>, -{ - pub async fn resolve(self) -> T { - match self { - LinkedObject::Object(o) => o, - LinkedObject::Link(l) => - reqwest::get(l.href()) - .await.unwrap() - .json::() - .await.unwrap(), - } - } -} - impl Link for String { fn href(&self) -> &str { self @@ -53,14 +41,3 @@ impl Link for serde_json::Value { // ... TODO! } - -impl From for LinkedObject { - fn from(value: serde_json::Value) -> Self { - if value.is_string() || value.get("href").is_some() { - Self::Link(Box::new(value)) - } else { - Self::Object(value) - } - } -} - diff --git a/src/activitystream/mod.rs b/src/activitystream/mod.rs index 6f2c4df5..6bb53560 100644 --- a/src/activitystream/mod.rs +++ b/src/activitystream/mod.rs @@ -1,62 +1,48 @@ -pub mod object; -pub use object::Object; - - -pub mod actor; -pub use actor::Actor; - - -pub mod activity; -pub use activity::Activity; - - pub mod types; -pub use types::{BaseType, ObjectType, ActivityType, ActorType}; - pub mod link; -pub use link::{Link, LinkedObject}; +pub use link::{Link, LinkType}; -pub trait ToJson : Object { - fn json(&self) -> serde_json::Value; -} +pub mod object; +pub use object::{Object, ObjectType}; -impl ToJson for T where T : Object { - fn json(&self) -> serde_json::Value { - let mut map = serde_json::Map::new(); - let mp = &mut map; +pub mod node; +pub use node::Node; - put_str(mp, "id", self.id()); - put_str(mp, "attributedTo", self.attributed_to()); - put_str(mp, "name", self.name()); - put_str(mp, "summary", self.summary()); - put_str(mp, "content", self.content()); +use crate::strenum; - if let Some(t) = self.full_type() { - map.insert( - "type".to_string(), - serde_json::Value::String(format!("{t:?}")), - ); - } +strenum! { + pub enum BaseType { + Invalid - if let Some(published) = self.published() { - map.insert( - "published".to_string(), - serde_json::Value::String(published.to_rfc3339()), - ); - } - - // ... TODO! - - serde_json::Value::Object(map) + Object(ObjectType), + Link(LinkType) } } -fn put_str(map: &mut serde_json::Map, k: &str, v: Option<&str>) { - if let Some(v) = v { - map.insert( - k.to_string(), - serde_json::Value::String(v.to_string()), - ); +pub trait Base { + fn id(&self) -> Option<&str> { None } + fn base_type(&self) -> Option { None } +} + +impl Base for () {} + +impl Base for String { + fn id(&self) -> Option<&str> { + Some(self) + } + + fn base_type(&self) -> Option { + Some(BaseType::Link(LinkType::Link)) + } +} + +impl Base for serde_json::Value { + fn id(&self) -> Option<&str> { + self.get("id")?.as_str() + } + + fn base_type(&self) -> Option { + self.get("type")?.as_str()?.try_into().ok() } } diff --git a/src/activitystream/node.rs b/src/activitystream/node.rs new file mode 100644 index 00000000..8d70a3e8 --- /dev/null +++ b/src/activitystream/node.rs @@ -0,0 +1,166 @@ +#[derive(Debug, thiserror::Error)] +pub enum NodeResolutionError { + #[error("error fetching object: {0}")] + FetchError(#[from] reqwest::Error), + + #[error("empty array")] + EmptyArray, + + #[error("field not present")] + Empty, +} + +pub enum Node { + Array(Vec), + Object(T), + Link(Box), + Empty, +} + +impl From> for Node { + fn from(value: Option) -> Self { + match value { + Some(x) => Node::Object(x), + None => Node::Empty, + } + } +} + +impl Node { + pub fn first(&self) -> Option<&T> { + match self { + Node::Empty | Node::Link(_) => None, + Node::Object(x) => Some(x), + Node::Array(v) => v.first(), + } + } + + pub fn items(&self) -> Option> { + match self { + Node::Empty | Node::Link(_) => None, + Node::Object(x) => Some(vec![x]), + Node::Array(v) => + Some(v.iter().map(|x| &x).collect()), + } + } +} + +impl Node +where + T : super::Base +{ + pub fn id(&self) -> Option<&str> { + match self { + Node::Array(v) => v.first()?.id(), + Node::Link(x) => Some(x.href()), + Node::Object(x) => x.id(), + Node::Empty => None, + } + } +} + +impl Node +where + T : Clone + for<'de> serde::Deserialize<'de>, +{ + pub async fn resolve(self) -> Result { + match self { + Node::Empty => Err(NodeResolutionError::Empty), + Node::Object(object) => Ok(object), + Node::Array(array) => Ok( + array + .first() + .ok_or(NodeResolutionError::EmptyArray)? + .clone() + ), + Node::Link(link) => Ok( + reqwest::get(link.href()) + .await? + .json::() + .await? + ), + } + } +} + +pub trait NodeExtractor { + fn node(&self, id: &str) -> Node; + fn node_vec(&self, id: &str) -> Node; +} + +impl NodeExtractor for serde_json::Value { + fn node(&self, id: &str) -> Node { + match self.get(id) { + None => Node::Empty, + Some(x) => match Node::new(x.clone()) { + Err(e) => Node::Empty, + Ok(x) => x, + } + } + } + + fn node_vec(&self, id: &str) -> Node { + match self.get(id) { + None => Node::Empty, + Some(x) => match Node::many(x.clone()) { + Err(e) => Node::Empty, + Ok(x) => x, + } + } + } +} + +#[derive(Debug, thiserror::Error)] +#[error("json object is wrongly structured")] +pub struct JsonStructureError; + +impl Node { + pub fn new(value: serde_json::Value) -> Result { + if !(value.is_string() || value.is_object()) { + return Err(JsonStructureError); + } + if value.is_string() || value.get("href").is_some() { + Ok(Self::Link(Box::new(value))) + } else { + Ok(Self::Object(value)) + } + } + + pub fn many(value: serde_json::Value) -> Result, JsonStructureError> { + if let serde_json::Value::Array(arr) = value { + Ok( + arr + .into_iter() + .filter_map(|x| Self::new(x.clone()).ok()) + .collect() + ) + } else { + Ok(vec![Self::new(value)?]) + } + } +} + + +pub(crate) trait InsertStr { + fn insert_str(&mut self, k: &str, v: Option<&str>); + fn insert_timestr(&mut self, k: &str, t: Option>); +} +impl InsertStr for serde_json::Map { + fn insert_str(&mut self, k: &str, v: Option<&str>) { + if let Some(v) = v { + self.insert( + k.to_string(), + serde_json::Value::String(v.to_string()), + ); + } + } + + fn insert_timestr(&mut self, k: &str, t: Option>) { + if let Some(published) = t { + self.insert( + "published".to_string(), + serde_json::Value::String(published.to_rfc3339()), + ); + } + } +} diff --git a/src/activitystream/object.rs b/src/activitystream/object.rs deleted file mode 100644 index 35d8b100..00000000 --- a/src/activitystream/object.rs +++ /dev/null @@ -1,47 +0,0 @@ -pub trait Object { - fn id(&self) -> Option<&str> { None } - fn full_type(&self) -> Option { None } - fn attachment (&self) -> Option<&str> { None } - fn attributed_to (&self) -> Option<&str> { None } - fn audience (&self) -> Option<&str> { None } - fn content (&self) -> Option<&str> { None } - fn context (&self) -> Option<&str> { None } - fn name (&self) -> Option<&str> { None } - fn end_time (&self) -> Option<&str> { None } - fn generator (&self) -> Option<&str> { None } - fn icon (&self) -> Option<&str> { None } - fn image (&self) -> Option<&str> { None } - fn in_reply_to (&self) -> Option<&str> { None } - fn location (&self) -> Option<&str> { None } - fn preview (&self) -> Option<&str> { None } - fn published (&self) -> Option> { None } - fn replies (&self) -> Option<&str> { None } - fn start_time (&self) -> Option<&str> { None } - fn summary (&self) -> Option<&str> { None } - fn tag (&self) -> Option<&str> { None } - fn updated (&self) -> Option<&str> { None } - fn url (&self) -> Option<&str> { None } - fn to (&self) -> Option<&str> { None } - fn bto (&self) -> Option<&str> { None } - fn cc (&self) -> Option<&str> { None } - fn bcc (&self) -> Option<&str> { None } - fn media_type (&self) -> Option<&str> { None } - fn duration (&self) -> Option<&str> { None } -} - -/// impl for empty object -impl Object for () {} - -// TODO only Value::Object is a valid Object, but rn "asd" behaves like {} (both are valid...) -/// impl for any json value -impl Object for serde_json::Value { - fn id(&self) -> Option<&str> { - self.get("id")?.as_str() - } - - fn full_type(&self) -> Option { - self.get("type")?.as_str()?.try_into().ok() - } - - // ... TODO! -} diff --git a/src/activitystream/object/activity/accept.rs b/src/activitystream/object/activity/accept.rs new file mode 100644 index 00000000..5a693280 --- /dev/null +++ b/src/activitystream/object/activity/accept.rs @@ -0,0 +1,12 @@ +use crate::strenum; + +strenum! { + pub enum AcceptType { + Accept, + TentativeAccept + } +} + +pub trait Accept : super::Activity { + fn accept_type(&self) -> Option; +} diff --git a/src/activitystream/object/activity/ignore.rs b/src/activitystream/object/activity/ignore.rs new file mode 100644 index 00000000..fe6f3435 --- /dev/null +++ b/src/activitystream/object/activity/ignore.rs @@ -0,0 +1,12 @@ +use crate::strenum; + +strenum! { + pub enum IgnoreType { + Ignore, + Block + } +} + +pub trait Ignore : super::Activity { + fn ignore_type(&self) -> Option; +} diff --git a/src/activitystream/object/activity/intransitive.rs b/src/activitystream/object/activity/intransitive.rs new file mode 100644 index 00000000..2d513011 --- /dev/null +++ b/src/activitystream/object/activity/intransitive.rs @@ -0,0 +1,14 @@ +use crate::strenum; + +strenum! { + pub enum IntransitiveActivityType { + IntransitiveActivity, + Arrive, + Question, + Travel + } +} + +pub trait IntransitiveActivity : super::Activity { + fn intransitive_activity_type(&self) -> Option; +} diff --git a/src/activitystream/object/activity/mod.rs b/src/activitystream/object/activity/mod.rs new file mode 100644 index 00000000..c5b43ec7 --- /dev/null +++ b/src/activitystream/object/activity/mod.rs @@ -0,0 +1,72 @@ +pub mod accept; +pub use accept::{Accept, AcceptType}; + +pub mod ignore; +pub use ignore::{Ignore, IgnoreType}; + +pub mod intransitive; +pub use intransitive::{IntransitiveActivity, IntransitiveActivityType}; + +pub mod offer; +pub use offer::{Offer, OfferType}; + +pub mod reject; +pub use reject::{Reject, RejectType}; + +use crate::activitystream::node::NodeExtractor; +use crate::activitystream::Node; +use crate::strenum; + +strenum! { + pub enum ActivityType { + Activity, + Add, + Announce, + Create, + Delete, + Dislike, + Flag, + Follow, + Join, + Leave, + Like, + Listen, + Move, + Read, + Remove, + Undo, + Update, + View + + IntransitiveActivity(IntransitiveActivityType), + Accept(AcceptType), + Ignore(IgnoreType), + Offer(OfferType), + Reject(RejectType) + } +} + +pub trait Activity : super::Object { + fn activity_type(&self) -> Option { None } + fn actor(&self) -> Node { Node::Empty:: } + fn object(&self) -> Node { Node::Empty:: } + fn target(&self) -> Option<&str> { None } + fn result(&self) -> Node { Node::Empty:: } + fn origin(&self) -> Node { Node::Empty:: } + fn instrument(&self) -> Node { Node::Empty:: } +} + +impl Activity for serde_json::Value { + fn activity_type(&self) -> Option { + let serde_json::Value::String(t) = self.get("type")? else { return None }; + ActivityType::try_from(t.as_str()).ok() + } + + fn object(&self) -> Node { + self.node_vec("object") + } + + fn actor(&self) -> Node { + self.node_vec("actor") + } +} diff --git a/src/activitystream/object/activity/offer.rs b/src/activitystream/object/activity/offer.rs new file mode 100644 index 00000000..5422242c --- /dev/null +++ b/src/activitystream/object/activity/offer.rs @@ -0,0 +1,12 @@ +use crate::strenum; + +strenum! { + pub enum OfferType { + Offer, + Invite + } +} + +pub trait Offer : super::Activity { + fn offer_type(&self) -> Option; +} diff --git a/src/activitystream/object/activity/reject.rs b/src/activitystream/object/activity/reject.rs new file mode 100644 index 00000000..7e5dd664 --- /dev/null +++ b/src/activitystream/object/activity/reject.rs @@ -0,0 +1,12 @@ +use crate::strenum; + +strenum! { + pub enum RejectType { + Reject, + TentativeReject + } +} + +pub trait Reject : super::Activity { + fn reject_type(&self) -> Option; +} diff --git a/src/activitystream/object/actor.rs b/src/activitystream/object/actor.rs new file mode 100644 index 00000000..b39dede0 --- /dev/null +++ b/src/activitystream/object/actor.rs @@ -0,0 +1,28 @@ +use crate::strenum; + +strenum! { + pub enum ActorType { + Application, + Group, + Organization, + Person, + Object + } +} + +pub trait Profile : super::Object { + // not a Node because it's always embedded and one + fn describes(&self) -> Option { None:: } +} + +pub trait Actor : super::Object { + fn actor_type(&self) -> Option { None } +} + +impl Actor for serde_json::Value { + +} + +impl Profile for serde_json::Value { + +} diff --git a/src/activitystream/object/collection/mod.rs b/src/activitystream/object/collection/mod.rs new file mode 100644 index 00000000..d653bc17 --- /dev/null +++ b/src/activitystream/object/collection/mod.rs @@ -0,0 +1,32 @@ +pub mod page; +pub use page::CollectionPage; + +use crate::activitystream::Node; +use crate::strenum; + +strenum! { + pub enum CollectionType { + Collection, + CollectionPage, + OrderedCollection, + OrderedCollectionPage + } +} + +pub trait Collection : super::Object { + fn collection_type(&self) -> Option { None } + + fn total_items(&self) -> Option { None } + fn current(&self) -> Node { Node::Empty:: } + fn first(&self) -> Node { Node::Empty:: } + fn last(&self) -> Node { Node::Empty:: } + fn items(&self) -> Node { Node::Empty:: } +} + +impl Collection for serde_json::Value { + +} + +impl CollectionPage for serde_json::Value { + +} diff --git a/src/activitystream/object/collection/page.rs b/src/activitystream/object/collection/page.rs new file mode 100644 index 00000000..a2b01c9f --- /dev/null +++ b/src/activitystream/object/collection/page.rs @@ -0,0 +1,7 @@ +use crate::activitystream::Node; + +pub trait CollectionPage : super::Collection { + fn part_of(&self) -> Node { Node::Empty:: } + fn next(&self) -> Node { Node::Empty:: } + fn prev(&self) -> Node { Node::Empty:: } +} diff --git a/src/activitystream/object/document.rs b/src/activitystream/object/document.rs new file mode 100644 index 00000000..346c46e1 --- /dev/null +++ b/src/activitystream/object/document.rs @@ -0,0 +1,34 @@ +use crate::strenum; + +strenum! { + pub enum DocumentType { + Document, + Audio, + Image, + Page, + Video + } +} + +pub trait Document : super::Object { + fn document_type(&self) -> Option { None } +} + + +pub trait Place : super::Object { + fn accuracy(&self) -> Option { None } + fn altitude(&self) -> Option { None } + fn latitude(&self) -> Option { None } + fn longitude(&self) -> Option { None } + fn radius(&self) -> Option { None } + fn units(&self) -> Option<&str> { None } +} + +pub trait Image : Document {} + +impl Document for serde_json::Value { + +} + +impl Image for serde_json::Value {} + diff --git a/src/activitystream/object/mod.rs b/src/activitystream/object/mod.rs new file mode 100644 index 00000000..2b81f774 --- /dev/null +++ b/src/activitystream/object/mod.rs @@ -0,0 +1,129 @@ +pub mod actor; +pub use actor::{Actor, Profile, ActorType}; + +pub mod collection; +pub use collection::{Collection, CollectionPage, CollectionType}; + +pub mod document; +pub use document::{Document, Image, Place, DocumentType}; + +pub mod activity; +pub use activity::{Activity, ActivityType}; + +pub mod tombstone; +pub use tombstone::Tombstone; + +pub mod relationship; +pub use relationship::Relationship; + +use crate::strenum; + +use super::{node::NodeExtractor, Node}; + +strenum! { + pub enum ObjectType { + Object, + Article, + Event, + Note, + Place, + Profile, + Relationship, + Tombstone + + Activity(ActivityType), + Actor(ActorType), + Collection(CollectionType), + Document(DocumentType) + } +} + +pub trait Object : super::Base { + fn object_type(&self) -> Option { None } + fn attachment(&self) -> Node { Node::Empty:: } + fn attributed_to(&self) -> Node { Node::Empty:: } + fn audience(&self) -> Node { Node::Empty:: } + fn content(&self) -> Option<&str> { None } // TODO handle language maps + fn context(&self) -> Node { Node::Empty:: } + fn name(&self) -> Option<&str> { None } // also in link // TODO handle language maps + fn end_time(&self) -> Option> { None } + fn generator(&self) -> Node { Node::Empty:: } + fn icon(&self) -> Node { Node::Empty:: } + fn image(&self) -> Node { Node::Empty:: } + fn in_reply_to(&self) -> Node { Node::Empty:: } + fn location(&self) -> Node { Node::Empty:: } + fn preview(&self) -> Node { Node::Empty:: } // also in link + fn published(&self) -> Option> { None } + fn replies(&self) -> Node { Node::Empty:: } + fn start_time(&self) -> Option> { None } + fn summary(&self) -> Option<&str> { None } + fn tag(&self) -> Node { Node::Empty:: } + fn updated(&self) -> Option> { None } + fn url(&self) -> Option> { None::> } + fn to(&self) -> Node { Node::Empty:: } + fn bto(&self) -> Node { Node::Empty:: } + fn cc(&self) -> Node { Node::Empty:: } + fn bcc(&self) -> Node { Node::Empty:: } + fn media_type(&self) -> Option<&str> { None } // also in link + fn duration(&self) -> Option<&str> { None } // TODO how to parse xsd:duration ? +} + +impl Object for serde_json::Value { + fn object_type(&self) -> Option { + match self.base_type() { + Some(super::BaseType::Object(o)) => Some(o), + _ => None, + } + } + + fn attachment(&self) -> Node { + self.node_vec("attachment") + } + + fn attributed_to(&self) -> Node { + self.node_vec("attributedTo") + } + + fn audience(&self) -> Node { + self.node_vec("audience") + } + + fn content(&self) -> Option<&str> { + self.get("content")?.as_str() + } + + fn name(&self) -> Option<&str> { + self.get("name")?.as_str() + } + + fn end_time(&self) -> Option> { + Some( + chrono::DateTime::parse_from_rfc3339( + self + .get("endTime")? + .as_str()? + ) + .ok()? + .with_timezone(&chrono::Utc)) + } + + fn generator(&self) -> Node { + self.node_vec("generator") + } + + fn icon(&self) -> Node { + self.node_vec("icon") + } + + fn image(&self) -> Node { + self.node_vec("image") + } + + fn in_reply_to(&self) -> Node { + self.node_vec("inReplyTo") + } + + fn location(&self) -> Node { + self.node_vec("location") + } +} diff --git a/src/activitystream/object/relationship.rs b/src/activitystream/object/relationship.rs new file mode 100644 index 00000000..ccd9abe6 --- /dev/null +++ b/src/activitystream/object/relationship.rs @@ -0,0 +1,11 @@ +use crate::activitystream::Node; + +pub trait Relationship : super::Object { + fn subject(&self) -> Node { Node::Empty:: } + fn relationship(&self) -> Option<&str> { None } // TODO what does this mean??? + fn object(&self) -> Node { Node::Empty:: } +} + +impl Relationship for serde_json::Value { + // ... TODO +} diff --git a/src/activitystream/object/tombstone.rs b/src/activitystream/object/tombstone.rs new file mode 100644 index 00000000..39dc2861 --- /dev/null +++ b/src/activitystream/object/tombstone.rs @@ -0,0 +1,8 @@ +pub trait Tombstone : super::Object { + fn former_type(&self) -> Option { None } + fn deleted(&self) -> Option> { None } +} + +impl Tombstone for serde_json::Value { + // ... TODO +} diff --git a/src/activitystream/types.rs b/src/activitystream/types.rs index fcee7800..58e863cf 100644 --- a/src/activitystream/types.rs +++ b/src/activitystream/types.rs @@ -14,8 +14,9 @@ impl From for sea_orm::TryGetError { } } +#[macro_export] macro_rules! strenum { - ( $(pub enum $enum_name:ident { $($flat:ident),+ $($deep:ident($inner:ident)),*};)+ ) => { + ( $(pub enum $enum_name:ident { $($flat:ident),+ $($deep:ident($inner:ident)),*})+ ) => { $( #[derive(PartialEq, Eq, Debug, Clone, Copy)] pub enum $enum_name { @@ -33,7 +34,7 @@ macro_rules! strenum { } impl TryFrom<&str> for $enum_name { - type Error = TypeValueError; + type Error = $crate::activitystream::types::TypeValueError; fn try_from(value:&str) -> Result { match value { @@ -44,7 +45,7 @@ macro_rules! strenum { return Ok(Self::$deep(x)); } )* - Err(TypeValueError) + Err($crate::activitystream::types::TypeValueError) }, } } @@ -87,142 +88,3 @@ macro_rules! strenum { )* }; } - -strenum! { - pub enum BaseType { - Invalid - - Object(ObjectType), - Link(LinkType) - }; - - pub enum LinkType { - Base, - Mention - }; - - pub enum ObjectType { - Object, - Relationship, - Tombstone - - Activity(ActivityType), - Actor(ActorType), - Collection(CollectionType), - Status(StatusType) - }; - - pub enum ActorType { - Application, - Group, - Organization, - Person, - Object - }; - - pub enum StatusType { - Article, - Event, - Note, - Place, - Profile - - Document(DocumentType) - }; - - pub enum CollectionType { - Collection, - CollectionPage, - OrderedCollection, - OrderedCollectionPage - }; - - pub enum AcceptType { - Accept, - TentativeAccept - }; - - pub enum DocumentType { - Document, - Audio, - Image, - Page, - Video - }; - - pub enum ActivityType { - Activity, - Add, - Announce, - Create, - Delete, - Dislike, - Flag, - Follow, - Join, - Leave, - Like, - Listen, - Move, - Read, - Remove, - Undo, - Update, - View - - IntransitiveActivity(IntransitiveActivityType), - Accept(AcceptType), - Ignore(IgnoreType), - Offer(OfferType), - Reject(RejectType) - }; - - pub enum IntransitiveActivityType { - IntransitiveActivity, - Arrive, - Question, - Travel - }; - - pub enum IgnoreType { - Ignore, - Block - }; - - pub enum OfferType { - Offer, - Invite - }; - - pub enum RejectType { - Reject, - TentativeReject - }; -} - -#[cfg(test)] -mod test { - #[test] - fn assert_flat_types_serialize() { - let x = super::IgnoreType::Block; - assert_eq!("Block", >::as_ref(&x)); - } - - #[test] - fn assert_deep_types_serialize() { - let x = super::StatusType::Document(super::DocumentType::Page); - assert_eq!("Page", >::as_ref(&x)); - } - - #[test] - fn assert_flat_types_deserialize() { - let x = super::ActorType::try_from("Person").expect("could not deserialize"); - assert_eq!(super::ActorType::Person, x); - } - - #[test] - fn assert_deep_types_deserialize() { - let x = super::ActivityType::try_from("Invite").expect("could not deserialize"); - assert_eq!(super::ActivityType::Offer(super::OfferType::Invite), x); - } -}