hyaenidae/profiles/src/apub/actions/apub/mod.rs

211 lines
7.2 KiB
Rust

use crate::{Action, Context, Error, RecoverableError, Required};
use activitystreams::{base::AnyBase, prelude::*};
mod block;
mod follow;
mod like;
mod note;
mod person;
use block::{block, undo_block};
use follow::{accept_follow, follow, reject_follow, undo_accept_follow, undo_follow};
use like::{like, undo_like};
use note::{announce_note, create_note, delete_note, note, update_note};
use person::{create_person, delete_person, person, update_person};
pub(crate) fn ingest(
any_base: AnyBase,
context: &Context,
mut stack: Vec<AnyBase>,
) -> Result<(), Error> {
// Don't double-ingest
if context.apub.object(any_base.id().req()?)?.is_some() {
return Ok(());
}
match parse(any_base.clone(), context)? {
Ok(action) => {
action.perform(context)?;
if let Some(any_base) = stack.pop() {
context.spawner.process(any_base, stack);
}
}
Err(RecoverableError::MissingApub(url)) => {
stack.push(any_base);
context.spawner.download_apub(url, stack);
}
Err(RecoverableError::MissingImages(urls)) => {
stack.push(any_base);
context.spawner.download_images(urls, stack);
}
}
Ok(())
}
fn get_obj<T, Kind>(
any_base: AnyBase,
context: &Context,
f: impl FnOnce(T, &Context, AnyBase) -> Result<Result<Box<dyn Action>, RecoverableError>, Error>,
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error>
where
T: ActorAndObjectRefExt + ExtendsExt<Kind>,
<T as activitystreams::base::Extends<Kind>>::Error: From<serde_json::Error>,
Error: From<<T as activitystreams::base::Extends<Kind>>::Error>,
for<'de> Kind: serde::Deserialize<'de>,
Kind: serde::Serialize,
{
wrap(any_base, context, |activity: T, context| {
let object = activity.object().as_one().req()?;
let any_base = if object.kind_str().is_none() {
let id = object.id().req()?;
match context.apub.object(id)? {
Some(any_base) => any_base,
None => return Ok(Err(RecoverableError::MissingApub(id.to_owned()))),
}
} else {
object.clone()
};
(f)(activity, context, any_base.clone())
})
}
fn wrap<T, Kind>(
any_base: AnyBase,
context: &Context,
f: impl FnOnce(T, &Context) -> Result<Result<Box<dyn Action>, RecoverableError>, Error>,
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error>
where
T: ExtendsExt<Kind>,
<T as activitystreams::base::Extends<Kind>>::Error: From<serde_json::Error>,
Error: From<<T as activitystreams::base::Extends<Kind>>::Error>,
for<'de> Kind: serde::Deserialize<'de>,
Kind: serde::Serialize,
{
let activity: T = any_base.clone().extend()?.req()?;
let res = (f)(activity, context);
if res.as_ref().map(|r| r.is_ok()).unwrap_or(false) {
context.apub.store_object(&any_base)?;
}
res
}
fn parse(
any_base: AnyBase,
context: &Context,
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
match any_base.kind_str().req()? {
"Accept" => get_obj(
any_base,
context,
|accept, context, any_base| match any_base.kind_str().req()? {
"Follow" => wrap(any_base, context, |follow, context| {
accept_follow(&accept, &follow, context)
}),
_ => Err(Error::Invalid),
},
),
"Announce" => get_obj(
any_base,
context,
|announce, context, any_base| match any_base.kind_str().req()? {
"Note" => wrap(any_base, context, |note, context| {
announce_note(&announce, &note, context)
}),
_ => Err(Error::Invalid),
},
),
"Block" => wrap(any_base, context, |block_obj, context| {
block(&block_obj, context)
}),
"Create" => get_obj(
any_base,
context,
|create, context, any_base| match any_base.kind_str().req()? {
"Note" => wrap(any_base, context, |note, context| {
create_note(&create, &note, context)
}),
"Person" => wrap(any_base, context, |person, context| {
create_person(&create, &person, context)
}),
_ => Err(Error::Invalid),
},
),
"Delete" => get_obj(
any_base,
context,
|delete, context, any_base| match any_base.kind_str().req()? {
"Note" => wrap(any_base, context, |note, context| {
delete_note(&delete, &note, context)
}),
"Person" => wrap(any_base, context, |person, context| {
delete_person(&delete, &person, context)
}),
_ => Err(Error::Invalid),
},
),
"Follow" => wrap(any_base, context, |follow_obj, context| {
follow(&follow_obj, context)
}),
"Like" => wrap(any_base, context, |like_obj, context| {
like(&like_obj, context)
}),
"Note" => wrap(any_base, context, |note_obj, context| {
note(&note_obj, context)
}),
"Person" => wrap(any_base, context, |person_obj, context| {
person(&person_obj, context)
}),
"Reject" => get_obj(
any_base,
context,
|reject, context, any_base| match any_base.kind_str().req()? {
"Follow" => wrap(any_base, context, |follow, context| {
reject_follow(&reject, &follow, context)
}),
_ => Err(Error::Invalid),
},
),
"Undo" => get_obj(any_base, context, |undo, context, any_base| match any_base
.kind_str()
.req()?
{
"Block" => wrap(any_base, context, |block, context| {
undo_block(&undo, &block, context)
}),
"Accept" => get_obj(
any_base,
context,
|accept, context, any_base| match any_base.kind_str().req()? {
"Follow" => wrap(any_base, context, |follow, context| {
undo_accept_follow(&undo, &accept, &follow, context)
}),
_ => Err(Error::Invalid),
},
),
"Follow" => wrap(any_base, context, |follow, context| {
undo_follow(&undo, &follow, context)
}),
"Like" => wrap(any_base, context, |like, context| {
undo_like(&undo, &like, context)
}),
_ => Err(Error::Invalid),
}),
"Update" => get_obj(
any_base,
context,
|update, context, any_base| match any_base.kind_str().req()? {
"Note" => wrap(any_base, context, |note, context| {
update_note(&update, &note, context)
}),
"Person" => wrap(any_base, context, |person, context| {
update_person(&update, &person, context)
}),
_ => Err(Error::Invalid),
},
),
_ => Err(Error::Invalid),
}
}