diff --git a/flabk/src/astreams/mod.rs b/flabk/src/astreams/mod.rs index 34ae60c..4177c9f 100644 --- a/flabk/src/astreams/mod.rs +++ b/flabk/src/astreams/mod.rs @@ -1,24 +1,63 @@ -use async_trait::async_trait; -use flabk_derive::IRI; +use std::collections::HashMap; + +use flabk_derive::{Resolvable, IRI}; use serde::{Deserialize, Serialize}; use self::{ - context::Context, - note::Note, + context::APContext, resolve::ResolveError, - serde_ext::{expand_partial, pull_single_into_vec}, + serde_ext::{expand_partial, into_vec}, }; pub mod context; -pub mod note; pub mod resolve; mod serde_ext; #[derive(Clone, Copy, Serialize, Deserialize, Debug)] -pub enum ActivityKind { - Create, - Like, +pub enum ObjectKind { + Article, + Audio, + Document, + Event, + Image, Note, + Page, + Place, + Profile, + Relationship, + Tombstone, + Video, +} + +#[derive(Clone, Copy, Serialize, Deserialize, Debug)] +pub enum ActivityKind { + Accept, + Add, + Announce, + Arrive, + Block, + Create, + Delete, + Dislike, + Flag, + Follow, + Invite, + Join, + Leave, + Like, + Listen, + Move, + Offer, + Question, + Reject, + Read, + Remove, + TentativeReject, + TentativeAccept, + Travel, + Undo, + Update, + View, } #[derive(Debug, Clone)] @@ -35,45 +74,211 @@ impl From for ExpandError { } } -#[async_trait] -pub trait ExpandLD { - async fn expand(obj: &ObjectLD) -> Result; -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct ActivityLD { - #[serde(rename = "@context")] - pub context_uri: String, - pub kind: ActivityKind, - #[serde(rename = "actor")] - pub actor_uri: String, - #[serde(rename = "to")] - pub to_uris: Vec, - pub object: Option, +/// A Collection is a subtype of Object that +/// represents ordered or unordered sets of +/// Object or Link instances. +#[derive(Debug, Clone, Deserialize)] +pub struct Collection { + pub summary: Option, + #[serde(rename = "type")] + pub kind: String, + #[serde(rename = "totalItems")] + pub total_items: Vec, } #[derive(Debug, Clone, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Activity { - // #[serde(rename = "@context")] - // #[serde(deserialize_with = "expand_partial")] - // pub ap_context: Context, - pub id: String, +pub struct Attachment { #[serde(rename = "type")] - pub kind: ActivityKind, - #[serde(deserialize_with = "expand_partial")] - pub attributed_to: Actor, + pub kind: String, pub content: Option, - pub context: Option, - #[serde(deserialize_with = "pull_single_into_vec")] - pub to: Vec, - pub url: Option, + pub url: String, } -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct ObjectLD { +// TODO: maybe this can work as attachment w/ the ID being more like +// the LD IDs? +#[derive(Debug, Clone, Deserialize)] +pub struct Image { + #[serde(flatten)] + pub base: ObjectBase, + #[serde(rename = "type")] + pub kind: Option, + pub id: String, + #[serde(deserialize_with = "into_vec")] + pub url: Vec, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct Audience { + #[serde(rename = "type")] + pub kind: String, + pub name: String, +} + +#[derive(Clone, Copy, Debug, Deserialize)] +pub enum Unit { + #[serde(rename = "m")] + Meters, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct Location { + #[serde(rename = "type")] + pub kind: String, + pub name: String, + pub longitude: Option, + pub latitude: Option, + pub altitude: Option, + pub units: Unit, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct Preview { + #[serde(rename = "type")] + pub kind: String, + pub name: String, + pub duration: Option, + pub url: Link, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct APResult { + #[serde(rename = "type")] + pub kind: String, + pub name: String, +} + +/// ObjectBase contains base Activity Streams +/// members that all objects should have. Except kind. +/// +/// This object is intended to be used by inlining it into +/// other serializable/deserializable objects. +#[derive(Debug, Clone, Deserialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct ObjectBase { #[serde(rename = "@context")] - pub context_uri: Option, + #[serde(deserialize_with = "expand_partial")] + pub ap_context: APContext, + pub name: Option, + pub name_map: Option>, + #[serde(deserialize_with = "into_vec")] + pub attachment: Vec, + #[serde(deserialize_with = "into_vec")] + pub attributed_to: Vec, + pub audience: Option, + pub media_type: Option, + pub content: Option, + pub content_map: Option>, + pub context: Option, + pub start_time: Option, + pub end_time: Option, + pub generator: Option, + #[serde(deserialize_with = "into_vec")] + pub icon: Vec, + // omit in_reply_to + pub location: Option, + pub preview: Option, + pub published: Option, + pub updated: Option, + pub replies: Option>, // TODO: type + pub summary: Option, + pub summary_map: Option>, + #[serde(deserialize_with = "into_vec")] + pub tag: Vec, + // omit url for now: need to merge into_vec & expand_partial + #[serde(deserialize_with = "into_vec")] + pub to: Vec, + #[serde(deserialize_with = "into_vec")] + pub bto: Vec, + #[serde(deserialize_with = "into_vec")] + pub cc: Vec, + #[serde(deserialize_with = "into_vec")] + pub bcc: Vec, + pub duration: Option, +} + +#[derive(Debug, Clone, Deserialize, Resolvable, IRI, Default)] +pub struct Object { + #[serde(flatten)] + pub base: ObjectBase, + #[serde(rename = "type")] + pub kind: Option, + pub id: String, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct Link { + #[serde(rename = "type")] + pub kind: Option, + pub name: Option, + pub href: String, + #[serde(rename = "hreflang")] + pub href_lang: Option, + #[serde(rename = "mediaType")] + pub media_type: Option, + pub height: u32, + pub width: u32, +} + +/// An Activity is a subtype of Object that describes +/// some form of action that may happen, +/// is currently happening, or has already happened. +/// +/// The Activity type itself serves as an abstract base type +/// for all types of activities. +/// It is important to note that the Activity type itself +/// does not carry any specific semantics about the kind of action being taken. +#[derive(Debug, Clone, Deserialize, Resolvable)] +#[serde(rename_all = "camelCase")] +pub struct Activity { + #[serde(flatten)] + pub base: ObjectBase, + #[serde(rename = "type")] + pub kind: Option, + pub id: String, + #[serde(deserialize_with = "expand_partial")] + pub actor: Option, + #[serde(deserialize_with = "expand_partial")] + pub object: Option, + pub result: Option, + pub target: Option, + pub origin: Option, + pub instrument: Option, +} + +/// Instances of IntransitiveActivity are a subtype of Activity +/// representing intransitive actions. +#[derive(Debug, Clone, Deserialize, Resolvable)] +#[serde(rename_all = "camelCase")] +pub struct IntransitiveActivity { + #[serde(flatten)] + pub base: ObjectBase, + #[serde(rename = "type")] + pub kind: Option, + pub id: String, + #[serde(deserialize_with = "expand_partial")] + pub actor: Option, + pub result: Option, + pub target: Option, + pub origin: Option, + pub instrument: Option, +} +pub enum ActorType { + Application, + Group, + Organization, + Person, + Service, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct Basic { + #[serde(rename = "type")] + pub kind: Option, + pub name: Option, +} + +#[derive(Debug, Clone, Deserialize, IRI, Default, Resolvable)] +pub struct Actor { pub id: String, #[serde(rename = "type")] pub kind: Option, @@ -117,7 +322,7 @@ pub async fn test() { "url": "https://localhost/u/test/h/6Q8BFF8W6PZT2ddngZ" }"#; - let obj = serde_json::from_str::(obj).unwrap(); + let obj = serde_json::from_str::(obj).unwrap(); println!(); println!("{:#?}", obj); } diff --git a/flabk/src/astreams/note.rs b/flabk/src/astreams/note.rs deleted file mode 100644 index e502919..0000000 --- a/flabk/src/astreams/note.rs +++ /dev/null @@ -1,64 +0,0 @@ -use async_trait::async_trait; - -use super::{ - context::{self, Context}, - resolve, ActivityKind, Actor, ExpandError, ExpandLD, ObjectLD, -}; - -#[derive(Debug, Clone)] -pub struct Note { - pub id: String, - pub content: Option, - pub context: Option, - pub published_at: Option, - pub author: Actor, - pub to: Vec, - pub url: Option, - pub conversation: Option, -} - -#[async_trait] -impl ExpandLD for Note { - async fn expand(obj: &ObjectLD) -> Result { - let obj = obj.clone(); - let resolver = resolve::Resolver::new(); - if let Some(kind) = obj.kind { - if let ActivityKind::Note = kind { - Ok(Note { - id: obj.id, - content: obj.content, - published_at: obj.published, - author: obj - .attributed_to - .map(|id| Actor { id }) - .ok_or(ExpandError::NoAttribution)?, - to: obj.to_uris.into_iter().map(|id| Actor { id }).collect(), - url: obj.url, - context: obj.context, - conversation: obj.conversation, - }) - } else { - Err(ExpandError::InvalidKind(Some(kind))) - } - } else { - Err(ExpandError::InvalidKind(None)) - } - } -} - -impl Into for Note { - fn into(self) -> ObjectLD { - ObjectLD { - context_uri: Some(context::CONTEXT_ID.to_string()), - id: self.id.clone(), - kind: Some(ActivityKind::Note), - attributed_to: Some(self.author.id), - published: self.published_at, - content: self.content, - context: self.context, - conversation: self.conversation, - url: Some(self.id), - to_uris: self.to.into_iter().map(|a| a.id).collect(), - } - } -}