Merge branch 'activity-pub' into development
# Conflicts: # Cargo.lock # Cargo.toml # src/main.rs
This commit is contained in:
commit
e21c5a8132
|
@ -1640,6 +1640,20 @@ dependencies = [
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "magnetar_activity_streams"
|
||||||
|
version = "0.3.0-alpha"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"chrono",
|
||||||
|
"either",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "magnetar_calckey_fe"
|
name = "magnetar_calckey_fe"
|
||||||
version = "0.3.0-alpha"
|
version = "0.3.0-alpha"
|
||||||
|
|
|
@ -8,6 +8,7 @@ license = "AGPL-3.0-only"
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
".",
|
".",
|
||||||
|
"ext_activity_streams",
|
||||||
"ext_federation",
|
"ext_federation",
|
||||||
"ext_nodeinfo",
|
"ext_nodeinfo",
|
||||||
"ext_webfinger",
|
"ext_webfinger",
|
||||||
|
|
|
@ -13,6 +13,7 @@ async-trait = { workspace = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|
||||||
|
either = { workspace = true }
|
||||||
chrono = { workspace = true, features = ["serde"] }
|
chrono = { workspace = true, features = ["serde"] }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
url = { workspace = true, features = ["serde"] }
|
url = { workspace = true, features = ["serde"] }
|
||||||
|
|
|
@ -1 +1,88 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
use crate::link::Link;
|
||||||
|
|
||||||
|
pub mod link;
|
||||||
|
pub mod object;
|
||||||
|
pub mod recipe;
|
||||||
|
|
||||||
|
pub trait ObjectTyped: Clone + Debug + Sized + 'static {
|
||||||
|
fn get_type() -> &'static str;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! def_ld {
|
||||||
|
($obj_type: expr, $x: ty) => {
|
||||||
|
impl $crate::ObjectTyped for $x {
|
||||||
|
fn get_type() -> &'static str {
|
||||||
|
$obj_type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! ld_union {
|
||||||
|
($z: ident, $($item_name: ident as $item_type: ty),+) => {
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct $z {
|
||||||
|
$(
|
||||||
|
#[serde(flatten)]
|
||||||
|
$item_name: Box<$item_type>,
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum OneOrMore<T> {
|
||||||
|
One(T),
|
||||||
|
Many(Vec<T>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> OneOrMore<T> {
|
||||||
|
pub fn into_vec(self) -> Vec<T> {
|
||||||
|
match self {
|
||||||
|
OneOrMore::One(x) => vec![x],
|
||||||
|
OneOrMore::Many(x) => x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> std::slice::Iter<'_, T> {
|
||||||
|
match self {
|
||||||
|
OneOrMore::One(x) => std::slice::from_ref(x).iter(),
|
||||||
|
OneOrMore::Many(x) => x.iter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||||
|
pub struct Base {
|
||||||
|
#[serde(rename = "@type")]
|
||||||
|
as_type: Option<OneOrMore<String>>,
|
||||||
|
#[serde(rename = "@id")]
|
||||||
|
as_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum LinkOrUrl<'a> {
|
||||||
|
Url(Url),
|
||||||
|
Link(Box<Link<'a>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Id(pub String);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub enum ObjectSlot<'a, T: ObjectTyped> {
|
||||||
|
Id(Id),
|
||||||
|
Value(Cow<'a, T>),
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
use crate::def_ld;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct LinkMention {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Mention", LinkMention);
|
|
@ -0,0 +1,44 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{def_ld, ObjectSlot};
|
||||||
|
use crate::object::ObjectLinkUnion;
|
||||||
|
use crate::OneOrMore;
|
||||||
|
|
||||||
|
pub mod link_types;
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Link<'a> {
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#href")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub href: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#hreflang")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub href_lang: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#mediaType")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub media_type: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#rel")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub rel: Option<OneOrMore<String>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#name")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub name: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#width")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub width: Option<u32>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#height")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub height: Option<u32>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#preview")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub preview: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Link", Link<'_>);
|
|
@ -0,0 +1,211 @@
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{def_ld, ObjectSlot, ObjectTyped, OneOrMore};
|
||||||
|
use crate::object::ObjectLinkUnion;
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Activity<'a> {
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#actor")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub actor: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#object")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub object: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#target")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub target: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#result")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub result: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#origin")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub origin: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#instrument")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub instrument: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Activity", Activity<'_>, base as Object);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct IntransitiveActivity {}
|
||||||
|
|
||||||
|
def_ld!(
|
||||||
|
"https://www.w3.org/ns/activitystreams#IntransitiveActivity",
|
||||||
|
IntransitiveActivity,
|
||||||
|
base as Activity
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityAccept {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Accept", ActivityAccept, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityTentativeAccept {}
|
||||||
|
|
||||||
|
def_ld!(
|
||||||
|
"https://www.w3.org/ns/activitystreams#TentativeAccept",
|
||||||
|
ActivityTentativeAccept,
|
||||||
|
base as ActivityAccept
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityAdd;
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Add", ActivityAdd, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityArrive;
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Arrive", ActivityArrive, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityCreate;
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Create", ActivityCreate, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityDelete {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Delete", ActivityDelete, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityFollow {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Follow", ActivityFollow, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityIgnore {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Ignore", ActivityIgnore, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityJoin {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Join", ActivityJoin, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityLeave {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Leave", ActivityLeave, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityLike {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Like", ActivityLike, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityOffer {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Offer", ActivityOffer, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityInvite {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Invite", ActivityInvite, base as ActivityOffer);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityReject {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Reject", ActivityReject, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityTentativeReject {}
|
||||||
|
|
||||||
|
def_ld!(
|
||||||
|
"https://www.w3.org/ns/activitystreams#TentativeReject",
|
||||||
|
ActivityTentativeReject,
|
||||||
|
base as ActivityReject
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityRemove {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Remove", ActivityRemove, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityUndo {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Undo", ActivityUndo, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityUpdate {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Update", ActivityUpdate, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityView {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#View", ActivityView, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityListen {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Listen", ActivityListen, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityRead {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Read", ActivityRead, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityMove {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Move", ActivityMove, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityTravel {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Travel", ActivityTravel, base as IntransitiveActivity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityAnnounce {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Announce", ActivityAnnounce, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityBlock {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Block", ActivityBlock, base as ActivityIgnore);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityFlag {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Flag", ActivityFlag, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityDislike {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Dislike", ActivityDislike, base as Activity);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum ActivityClosedStatus<'a> {
|
||||||
|
Date(DateTime<Utc>),
|
||||||
|
Bool(bool),
|
||||||
|
Other(ObjectSlot<'a, ObjectLinkUnion>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActivityQuestion<'a> {
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#oneOf")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub one_of: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#anyOf")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub any_of: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#closed")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub closed: Option<OneOrMore<ActivityClosedStatus<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Question", ActivityQuestion<'_>, base as IntransitiveActivity);
|
|
@ -0,0 +1,65 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use crate::{def_ld, ObjectSlot};
|
||||||
|
use crate::object::{Collection, ObjectLinkUnion, OrderedCollection};
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActorActivityPubProps<'a> {
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#inbox")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub inbox: Option<ObjectSlot<'a, OrderedCollection>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#outbox")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub outbox: Option<ObjectSlot<'a, OrderedCollection>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#following")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub following: Option<ObjectSlot<'a, Collection>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#followers")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub followers: Option<ObjectSlot<'a, Collection>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#liked")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub liked: Option<ObjectSlot<'a, Collection>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#shares")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub shares: Option<ObjectSlot<'a, Collection>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#preferredUsername")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub preferred_username: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#endpoints")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub endpoints: Option<Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActorApplication {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Application", ActorApplication, base as Object);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActorGroup {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Group", ActorGroup, base as Object);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActorOrganization {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Organization", ActorOrganization, base as Object);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActorPerson {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Person", ActorPerson, base as Object);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActorService {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Service", ActorService, base as Object);
|
|
@ -0,0 +1,226 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{ld_union, LinkOrUrl, ObjectSlot, OneOrMore};
|
||||||
|
use crate::def_ld;
|
||||||
|
use crate::link::Link;
|
||||||
|
use crate::object::object_types::ObjectImageLinkUnion;
|
||||||
|
|
||||||
|
pub mod activity;
|
||||||
|
pub mod actor;
|
||||||
|
pub mod object_types;
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Object<'a> {
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#attachment")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub attachment: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#attributedTo")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub attributed_to: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#audience")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub audience: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#content")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub content: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#contentMap")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub content_map: Option<HashMap<String, String>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#context")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub context: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#name")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub name: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#nameMap")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub name_map: Option<HashMap<String, String>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#endTime")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub end_time: Option<DateTime<Utc>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#generator")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub generator: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#icon")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub icon: Option<OneOrMore<ObjectSlot<'a, ObjectImageLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#image")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub image: Option<OneOrMore<ObjectSlot<'a, ObjectImageLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#inReplyTo")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub in_reply_to: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#location")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub location: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#preview")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub preview: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#published")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub published: Option<DateTime<Utc>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#replies")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub replies: Option<ObjectSlot<'a, CollectionLinkUnion>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#startTime")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub start_time: Option<DateTime<Utc>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#summary")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub summary: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#summaryMap")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub summary_map: Option<HashMap<String, String>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#tag")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub tag: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#updated")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub updated: Option<DateTime<Utc>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#url")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub url: Option<OneOrMore<LinkOrUrl<'a>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#to")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub to: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#bto")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub bto: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#cc")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub cc: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#bcc")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub bcc: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#mediaType")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub media_type: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#duration")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub duration: Option<String>,
|
||||||
|
|
||||||
|
// ActivityPub
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#source")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub source: Option<ObjectSlot<'a, Object<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Object", Object<'_>);
|
||||||
|
ld_union!(
|
||||||
|
ObjectLinkUnion,
|
||||||
|
object_props as Object,
|
||||||
|
link_props as Link
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Collection {
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#totalItems")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub total_items: Option<u32>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#current")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub current: Option<RefCollectionPageLinkUnion>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#first")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub first: Option<RefCollectionPageLinkUnion>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#last")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub last: Option<RefCollectionPageLinkUnion>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#items")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub items: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
def_ld!(
|
||||||
|
"https://www.w3.org/ns/activitystreams#Collection",
|
||||||
|
Collection
|
||||||
|
);
|
||||||
|
ld_union!(
|
||||||
|
CollectionLinkUnion,
|
||||||
|
collection_props as Collection,
|
||||||
|
link_props as Link
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct OrderedCollection {
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#orderedItems")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub ordered_items: Option<OneOrMore<RefObjectLinkUnion>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
def_ld!(
|
||||||
|
"https://www.w3.org/ns/activitystreams#OrderedCollection",
|
||||||
|
OrderedCollection
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct CollectionPage {
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#next")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub next: Option<RefCollectionPageLinkUnion>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#prev")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub prev: Option<RefCollectionPageLinkUnion>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#partOf")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub part_of: Option<RefCollectionLinkUnion>,
|
||||||
|
}
|
||||||
|
|
||||||
|
def_ld!(
|
||||||
|
"https://www.w3.org/ns/activitystreams#CollectionPage",
|
||||||
|
CollectionPage
|
||||||
|
);
|
||||||
|
ld_union!(
|
||||||
|
CollectionPageLinkUnion,
|
||||||
|
page_props as CollectionPage,
|
||||||
|
link_props as Link
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct OrderedCollectionPage {
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#startIndex")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub start_index: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
def_ld!(
|
||||||
|
"https://www.w3.org/ns/activitystreams#OrderedCollectionPage",
|
||||||
|
OrderedCollectionPage
|
||||||
|
);
|
|
@ -0,0 +1,119 @@
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{def_ld, ld_union, ObjectSlot, ObjectTyped, OneOrMore};
|
||||||
|
use crate::link::Link;
|
||||||
|
use crate::object::{Object, ObjectLinkUnion};
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ObjectRelationship<'a> {
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#object")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub object: Option<OneOrMore<ObjectSlot<'a, ObjectLinkUnion>>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#subject")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub subject: Option<ObjectSlot<'a, ObjectLinkUnion>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#relationship")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub relationship: Option<OneOrMore<ObjectSlot<'a, Object<'a>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Relationship", ObjectRelationship, base as Object);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ObjectArticle {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Article", ObjectArticle, base as Object);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ObjectDocument {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Document", ObjectDocument, base as Object);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ObjectAudio {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Audio", ObjectAudio, base as ObjectDocument);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ObjectImage {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Image", ObjectImage, base as ObjectDocument);
|
||||||
|
ld_union!(
|
||||||
|
ObjectImageLinkUnion,
|
||||||
|
image_props as ObjectImage,
|
||||||
|
link_props as Link
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ObjectVideo {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Video", ObjectVideo, base as ObjectDocument);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ObjectNote {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Note", ObjectNote, base as Object);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ObjectPage {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Page", ObjectPage, base as ObjectDocument);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ObjectEvent {}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Event", ObjectEvent, base as Object);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ObjectPlace {
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#accuracy")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub accuracy: Option<f64>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#altitude")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub altitude: Option<f64>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#latitude")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub latitude: Option<f64>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#longitude")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub longitude: Option<f64>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#radius")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub radius: Option<f64>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#units")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub units: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Place", ObjectPlace, base as Object);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ObjectProfile<'a> {
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#describes")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub describes: Option<ObjectSlot<'a, ObjectProfile<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Profile", ObjectProfile, base as Object);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ObjectTombstone {
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#formerType")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub former_type: Option<OneOrMore<String>>,
|
||||||
|
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#deleted")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub deleted: Option<DateTime<Utc>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
def_ld!("https://www.w3.org/ns/activitystreams#Tombstone", ObjectTombstone, base as Object);
|
|
@ -0,0 +1,87 @@
|
||||||
|
use crate::object::activity::ActivityBlock;
|
||||||
|
|
||||||
|
use crate::object::Object;
|
||||||
|
use crate::recipe::RecipeError;
|
||||||
|
use crate::{extract_field, Id, ObjectRaw};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Block {
|
||||||
|
pub id: Id,
|
||||||
|
pub actor: Id,
|
||||||
|
pub object: Id,
|
||||||
|
pub to: Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Block {
|
||||||
|
fn parse(object: &ObjectRaw<impl AsRef<Object>>) -> Result<Block, RecipeError> {
|
||||||
|
let json = object.as_json();
|
||||||
|
let data: &Object = (*object.data).as_ref();
|
||||||
|
|
||||||
|
let ap_type = extract_field!(data, as_type, unwrap);
|
||||||
|
if ap_type.iter().all(|t| t != "Block") {
|
||||||
|
return Err(RecipeError::UnexpectedType(format!("{:?}", ap_type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let block = ActivityBlock::deserialize(json)?;
|
||||||
|
Ok(Block {
|
||||||
|
id: Id(extract_field!(block, as_id, unwrap | owned)),
|
||||||
|
actor: Id(extract_field!(
|
||||||
|
block,
|
||||||
|
actor,
|
||||||
|
unwrap | single | as_id | owned
|
||||||
|
)),
|
||||||
|
object: Id(extract_field!(
|
||||||
|
block,
|
||||||
|
object,
|
||||||
|
unwrap | single | as_id | owned
|
||||||
|
)),
|
||||||
|
to: Id(extract_field!(block, to, unwrap | single | as_id | owned)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::object::activity::{Activity, ActivityBlock, ActivityIgnore};
|
||||||
|
use crate::object::Object;
|
||||||
|
use crate::ObjectRaw;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_json() {
|
||||||
|
let json = json!({
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id": "https://a.example.com/blocks/1",
|
||||||
|
"type": "Block",
|
||||||
|
"actor": "https://a.example.com/users/1",
|
||||||
|
"object": "https://b.example.com/users/2",
|
||||||
|
"to": "https://b.example.com/users/2",
|
||||||
|
});
|
||||||
|
|
||||||
|
let object = ObjectRaw::<Activity>::deserialize(json).unwrap();
|
||||||
|
|
||||||
|
let block = super::Block::parse(&object).unwrap();
|
||||||
|
|
||||||
|
println!("{:#?}", block);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_json_with_multiple_types() {
|
||||||
|
let json = json!({
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id": "https://a.example.com/blocks/1",
|
||||||
|
"type": ["Block", "https://example.com/SomeOtherType"],
|
||||||
|
"actor": "https://a.example.com/users/1",
|
||||||
|
"object": "https://b.example.com/users/2",
|
||||||
|
"to": "https://b.example.com/users/2",
|
||||||
|
});
|
||||||
|
|
||||||
|
let object = ObjectRaw::<Activity>::deserialize(json).unwrap();
|
||||||
|
|
||||||
|
let block = super::Block::parse(&object).unwrap();
|
||||||
|
|
||||||
|
println!("{:#?}", block);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::OneOrMore;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
pub mod block;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum RecipeError {
|
||||||
|
#[error("Serde error")]
|
||||||
|
DeserializationError(#[from] serde_json::Error),
|
||||||
|
#[error("Unexpected type: {0}")]
|
||||||
|
UnexpectedType(String),
|
||||||
|
#[error("Missing field: {0}")]
|
||||||
|
MissingField(&'static str),
|
||||||
|
#[error("Single item expected: {0}")]
|
||||||
|
ExpectedSingleField(&'static str),
|
||||||
|
#[error("Missing ID")]
|
||||||
|
MissingId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub struct FieldPipeline<T> {
|
||||||
|
value: T,
|
||||||
|
field_name: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! extract_field {
|
||||||
|
($obj: expr, $field: ident, $($step:ident)|+ $(|)?) => {{
|
||||||
|
let pipeline = $crate::recipe::FieldPipeline {
|
||||||
|
value: $obj.$field.as_ref(),
|
||||||
|
field_name: stringify!($field),
|
||||||
|
};
|
||||||
|
|
||||||
|
$(
|
||||||
|
let pipeline = pipeline.$step()?;
|
||||||
|
)+
|
||||||
|
|
||||||
|
pipeline.into_inner()
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FieldPipeline<Option<T>> {
|
||||||
|
fn unwrap(self) -> Result<FieldPipeline<T>, RecipeError> {
|
||||||
|
Ok(FieldPipeline {
|
||||||
|
value: self
|
||||||
|
.value
|
||||||
|
.ok_or(RecipeError::MissingField(self.field_name))?,
|
||||||
|
field_name: self.field_name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resolvable> FieldPipeline<&T> {
|
||||||
|
fn as_id(&self) -> Result<FieldPipeline<&str>, RecipeError> {
|
||||||
|
Ok(FieldPipeline {
|
||||||
|
value: self.value.unresolve().ok_or(RecipeError::MissingId)?,
|
||||||
|
field_name: self.field_name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> FieldPipeline<&'a OneOrMore<T>> {
|
||||||
|
fn single(self) -> Result<FieldPipeline<&'a T>, RecipeError> {
|
||||||
|
Ok(FieldPipeline {
|
||||||
|
value: match self.value {
|
||||||
|
OneOrMore::One(x) => Ok(x),
|
||||||
|
OneOrMore::Many(vec) if vec.len() == 1 => Ok(vec.first().unwrap()),
|
||||||
|
OneOrMore::Many(_) => Err(RecipeError::ExpectedSingleField(self.field_name)),
|
||||||
|
}?,
|
||||||
|
field_name: self.field_name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ToOwned + ?Sized> FieldPipeline<&T> {
|
||||||
|
fn owned(&self) -> Result<FieldPipeline<T::Owned>, RecipeError> {
|
||||||
|
Ok(FieldPipeline {
|
||||||
|
value: self.value.to_owned(),
|
||||||
|
field_name: self.field_name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FieldPipeline<T> {
|
||||||
|
fn into_inner(self) -> T {
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
use futures_util::stream::StreamExt;
|
||||||
|
use magnetar_activity_pub::Resolver;
|
||||||
|
use magnetar_core::web_model::content_type::ContentActivityStreams;
|
||||||
|
use reqwest::Client;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_json::Value;
|
||||||
|
use thiserror::Error;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct DereferencingClient {
|
||||||
|
pub client: Client,
|
||||||
|
pub body_limit: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum DereferencingClientBuilderError {
|
||||||
|
#[error("Reqwest error: {0}")]
|
||||||
|
ReqwestError(#[from] reqwest::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum DereferencingClientError {
|
||||||
|
#[error("Reqwest error: {0}")]
|
||||||
|
ReqwestError(#[from] reqwest::Error),
|
||||||
|
#[error("JSON error: {0}")]
|
||||||
|
JsonError(#[from] serde_json::Error),
|
||||||
|
#[error("Body limit exceeded error")]
|
||||||
|
BodyLimitExceededError,
|
||||||
|
#[error("Invalid URL: {0}")]
|
||||||
|
InvalidUrl(#[from] url::ParseError),
|
||||||
|
#[error("Client error: {0}")]
|
||||||
|
Other(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DereferencingClient {
|
||||||
|
pub fn new(
|
||||||
|
force_https: bool,
|
||||||
|
body_limit: usize,
|
||||||
|
) -> Result<DereferencingClient, reqwest::Error> {
|
||||||
|
let client = Client::builder().https_only(force_https).build()?;
|
||||||
|
|
||||||
|
Ok(DereferencingClient { client, body_limit })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn dereference(&self, url: Url) -> Result<Value, DereferencingClientError> {
|
||||||
|
let mut headers = reqwest::header::HeaderMap::new();
|
||||||
|
|
||||||
|
headers.insert(
|
||||||
|
reqwest::header::ACCEPT,
|
||||||
|
reqwest::header::HeaderValue::from_static(ContentActivityStreams.as_ref()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let response = self
|
||||||
|
.client
|
||||||
|
.get(url)
|
||||||
|
.headers(headers)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(DereferencingClientError::ReqwestError)?;
|
||||||
|
|
||||||
|
let mut body = response.bytes_stream();
|
||||||
|
|
||||||
|
let mut data = Vec::with_capacity(4096);
|
||||||
|
|
||||||
|
while let Some(buf) = body.next().await {
|
||||||
|
let chunk = buf.map_err(DereferencingClientError::ReqwestError)?;
|
||||||
|
|
||||||
|
if data.len() + chunk.len() > self.body_limit {
|
||||||
|
return Err(DereferencingClientError::BodyLimitExceededError);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.extend_from_slice(&chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
let json =
|
||||||
|
serde_json::from_slice::<Value>(&data).map_err(DereferencingClientError::JsonError)?;
|
||||||
|
|
||||||
|
Ok(json)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl Resolver for DereferencingClient {
|
||||||
|
type Error = DereferencingClientError;
|
||||||
|
|
||||||
|
async fn resolve<T: for<'a> Deserialize<'a>>(&self, id: &str) -> Result<T, Self::Error> {
|
||||||
|
let url = id.parse().map_err(DereferencingClientError::InvalidUrl)?;
|
||||||
|
|
||||||
|
let json = self.dereference(url).await?;
|
||||||
|
|
||||||
|
serde_json::from_value(json).map_err(DereferencingClientError::JsonError)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
use axum::http::{HeaderMap, Method};
|
||||||
|
use hyper::client::HttpConnector;
|
||||||
|
use hyper::http::uri;
|
||||||
|
use hyper::{header, Body, Client, Request, Response, Uri};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ProxyClientError {
|
||||||
|
#[error("Client error: {0}")]
|
||||||
|
ClientError(String),
|
||||||
|
#[error("URL error: {0}")]
|
||||||
|
UriError(#[from] uri::InvalidUri),
|
||||||
|
#[error("HTTP error: {0}")]
|
||||||
|
HttpError(#[from] axum::http::Error),
|
||||||
|
#[error("Hyper error: {0}")]
|
||||||
|
HyperError(#[from] hyper::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ProxyClient {
|
||||||
|
pub client: Client<HttpConnector, Body>,
|
||||||
|
pub upstream: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProxyClient {
|
||||||
|
pub fn new(upstream: String) -> ProxyClient {
|
||||||
|
let client = Client::builder().set_host(false).build_http();
|
||||||
|
|
||||||
|
ProxyClient { client, upstream }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send(
|
||||||
|
&self,
|
||||||
|
host: &str,
|
||||||
|
path: &str,
|
||||||
|
method: Method,
|
||||||
|
body: Body,
|
||||||
|
headers_in: &HeaderMap,
|
||||||
|
) -> Result<Response<Body>, ProxyClientError> {
|
||||||
|
let mut builder = Request::builder();
|
||||||
|
|
||||||
|
let Some(headers) = builder.headers_mut() else {
|
||||||
|
return Err(ProxyClientError::ClientError("No headers".to_owned()));
|
||||||
|
};
|
||||||
|
|
||||||
|
*headers = headers_in.clone();
|
||||||
|
|
||||||
|
headers.insert(
|
||||||
|
header::HOST,
|
||||||
|
host.parse().map_err(|e| {
|
||||||
|
ProxyClientError::ClientError(format!("Invalid header value: {e:?}"))
|
||||||
|
})?,
|
||||||
|
);
|
||||||
|
|
||||||
|
let uri = format!("{}/{}", self.upstream, path)
|
||||||
|
.parse::<Uri>()
|
||||||
|
.map_err(ProxyClientError::UriError)?;
|
||||||
|
|
||||||
|
let request = builder
|
||||||
|
.method(method)
|
||||||
|
.uri(uri)
|
||||||
|
.body(body)
|
||||||
|
.map_err(ProxyClientError::HttpError)?;
|
||||||
|
|
||||||
|
let response = self
|
||||||
|
.client
|
||||||
|
.request(request)
|
||||||
|
.await
|
||||||
|
.map_err(ProxyClientError::HyperError)?;
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod dereferencing_client;
|
||||||
|
pub mod forwarding_client;
|
Loading…
Reference in New Issue