317 lines
8.1 KiB
Rust
317 lines
8.1 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use flabk_derive::LD;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::astreams::resolve::Resolvable;
|
|
|
|
use self::{
|
|
resolve::ResolveError,
|
|
serde_ext::{expand_partial, expand_partial_into_vec, into_vec},
|
|
};
|
|
|
|
pub mod context;
|
|
pub mod resolve;
|
|
mod serde_ext;
|
|
|
|
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
|
|
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)]
|
|
pub enum ExpandError {
|
|
InvalidKind(Option<ActivityKind>),
|
|
NoAttribution,
|
|
ResolveIRI(String),
|
|
Other(String),
|
|
}
|
|
|
|
impl From<ResolveError> for ExpandError {
|
|
fn from(err: ResolveError) -> Self {
|
|
Self::ResolveIRI(err.0)
|
|
}
|
|
}
|
|
|
|
/// 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<T> {
|
|
pub summary: Option<String>,
|
|
#[serde(rename = "type")]
|
|
pub kind: String,
|
|
#[serde(rename = "totalItems")]
|
|
pub total_items: Vec<T>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct Attachment {
|
|
#[serde(rename = "type")]
|
|
pub kind: String,
|
|
pub content: Option<String>,
|
|
pub url: String,
|
|
}
|
|
|
|
// 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<ObjectKind>,
|
|
pub id: String,
|
|
#[serde(deserialize_with = "into_vec")]
|
|
pub url: Vec<Link>,
|
|
}
|
|
|
|
#[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<f64>,
|
|
pub latitude: Option<f64>,
|
|
pub altitude: Option<f64>,
|
|
pub units: Unit,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct Preview {
|
|
#[serde(rename = "type")]
|
|
pub kind: String,
|
|
pub name: String,
|
|
pub duration: Option<String>,
|
|
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")]
|
|
// #[serde(deserialize_with = "expand_partial")]
|
|
// pub ap_context: APContext,
|
|
pub name: Option<String>,
|
|
pub name_map: Option<HashMap<String, String>>,
|
|
#[serde(deserialize_with = "into_vec", default = "Vec::new")]
|
|
pub attachment: Vec<Attachment>,
|
|
#[serde(deserialize_with = "expand_partial_into_vec")]
|
|
pub attributed_to: Vec<Actor>,
|
|
pub audience: Option<Audience>,
|
|
pub media_type: Option<String>,
|
|
pub content: Option<String>,
|
|
pub content_map: Option<HashMap<String, String>>,
|
|
pub context: Option<String>,
|
|
pub start_time: Option<String>,
|
|
pub end_time: Option<String>,
|
|
pub generator: Option<Actor>,
|
|
#[serde(deserialize_with = "into_vec", default = "Vec::new")]
|
|
pub icon: Vec<Link>,
|
|
// omit in_reply_to
|
|
pub location: Option<Location>,
|
|
pub preview: Option<Preview>,
|
|
pub published: Option<String>,
|
|
pub updated: Option<String>,
|
|
pub replies: Option<Collection<()>>, // TODO: type
|
|
pub summary: Option<String>,
|
|
pub summary_map: Option<HashMap<String, String>>,
|
|
#[serde(deserialize_with = "expand_partial_into_vec", default = "Vec::new")]
|
|
pub tag: Vec<Actor>,
|
|
// omit url for now: need to merge into_vec & expand_partial
|
|
#[serde(deserialize_with = "into_vec", default = "Vec::new")]
|
|
pub to: Vec<String>,
|
|
#[serde(deserialize_with = "into_vec", default = "Vec::new")]
|
|
pub bto: Vec<String>,
|
|
#[serde(deserialize_with = "into_vec", default = "Vec::new")]
|
|
pub cc: Vec<String>,
|
|
#[serde(deserialize_with = "into_vec", default = "Vec::new")]
|
|
pub bcc: Vec<String>,
|
|
pub duration: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize, LD, Default)]
|
|
pub struct Object {
|
|
#[serde(flatten)]
|
|
pub base: ObjectBase,
|
|
#[serde(rename = "type")]
|
|
pub kind: Option<ObjectKind>,
|
|
pub id: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct Link {
|
|
#[serde(rename = "type")]
|
|
pub kind: Option<ActivityKind>,
|
|
pub name: Option<String>,
|
|
pub href: String,
|
|
#[serde(rename = "hreflang")]
|
|
pub href_lang: Option<String>,
|
|
#[serde(rename = "mediaType")]
|
|
pub media_type: Option<String>,
|
|
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)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct Activity {
|
|
#[serde(flatten)]
|
|
pub base: ObjectBase,
|
|
#[serde(rename = "type")]
|
|
pub kind: Option<ActivityKind>,
|
|
pub id: String,
|
|
#[serde(deserialize_with = "expand_partial")]
|
|
pub actor: Option<Actor>,
|
|
#[serde(deserialize_with = "expand_partial")]
|
|
pub object: Option<Object>,
|
|
pub result: Option<APResult>,
|
|
pub target: Option<Basic>,
|
|
pub origin: Option<Basic>,
|
|
pub instrument: Option<Basic>,
|
|
}
|
|
|
|
/// Instances of IntransitiveActivity are a subtype of Activity
|
|
/// representing intransitive actions.
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct IntransitiveActivity {
|
|
#[serde(flatten)]
|
|
pub base: ObjectBase,
|
|
#[serde(rename = "type")]
|
|
pub kind: Option<ActivityKind>,
|
|
pub id: String,
|
|
#[serde(deserialize_with = "expand_partial")]
|
|
pub actor: Option<Actor>,
|
|
pub result: Option<APResult>,
|
|
pub target: Option<Basic>,
|
|
pub origin: Option<Basic>,
|
|
pub instrument: Option<Basic>,
|
|
}
|
|
pub enum ActorType {
|
|
Application,
|
|
Group,
|
|
Organization,
|
|
Person,
|
|
Service,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
pub struct Basic {
|
|
#[serde(rename = "type")]
|
|
pub kind: Option<ActivityKind>,
|
|
pub name: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize, LD, Default)]
|
|
pub struct Actor {
|
|
pub id: String,
|
|
#[serde(rename = "type")]
|
|
pub kind: Option<ActivityKind>,
|
|
#[serde(rename = "attributedTo")]
|
|
pub attributed_to: Option<String>,
|
|
pub published: Option<String>,
|
|
pub content: Option<String>,
|
|
pub context: Option<String>,
|
|
pub conversation: Option<String>,
|
|
pub url: Option<String>,
|
|
#[serde(rename = "to")]
|
|
#[serde(deserialize_with = "into_vec")]
|
|
pub to_uris: Vec<String>,
|
|
}
|
|
|
|
pub async fn test() {
|
|
let obj = r#"{
|
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
"attributedTo": "http://localhost:3001/u/test",
|
|
"content": "honk donk",
|
|
"context": "data:,electrichonkytonk-2jqQ42HyJXctnBKTy1",
|
|
"conversation": "data:,electrichonkytonk-2jqQ42HyJXctnBKTy1",
|
|
"id": "htts://localhost:3001/u/test/h/6Q8BFF8W6PZT2ddngZ",
|
|
"published": "2022-09-30T19:04:45Z",
|
|
"summary": "",
|
|
"to": "https://www.w3.org/ns/activitystreams#Public",
|
|
"type": "Note",
|
|
"url": "https://localhost/u/test/h/6Q8BFF8W6PZT2ddngZ"
|
|
}"#;
|
|
|
|
let obj = serde_json::from_str::<Object>(obj).unwrap();
|
|
println!();
|
|
println!("{:#?}", obj);
|
|
}
|