flabk/flabk/src/astreams/mod.rs

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);
}