hermit/src/ap/mod.rs

148 lines
3.2 KiB
Rust

//! ActivityPub implementation code and related abstractions.
use futures::prelude::*;
use serde::Serialize;
use crate::{ Id, Activity, err, Result, Error, sign, ctx::Context, db::{ Post, Privacy } };
/// Represents the creation of an object.
///
/// Valid object types: [`Note`].
#[derive(Clone, Serialize)]
pub enum Create {
Note {
id: Id,
actor: Id,
object: Note,
}
}
impl TryFrom<Create> for Post {
type Error = Error;
fn try_from (value: Create) -> Result<Post> {
let privacy = Privacy::infer(&value);
match value {
Create::Note { id: create, object, actor } => {
let Note { content, id, .. } = object;
Ok (Post {
author: actor,
content,
privacy,
create,
id,
})
},
}
}
}
#[derive(Clone, Debug)]
pub enum Invalid {}
/// A follow request.
///
/// Valid object types: [`Actor`].
#[derive(Clone, Serialize)]
pub enum Follow {
Actor {
id: Id,
object: Actor,
}
}
/// Signal that a [follow request][Follow] has been accepted.
///
/// Valid object types: [`Follow`].
#[derive(Clone, Serialize)]
pub enum Accept {
Follow {
id: Id,
object: Follow,
}
}
/// An entity that publishes activities.
#[derive(Clone, Serialize, sqlx::FromRow, sqlx::Type)]
pub struct Actor {
id: Id,
}
/// Represents a [`Post`] for federation purposes.
#[derive(Clone, Serialize)]
pub struct Note {
id: Id,
content: Option<String>,
}
impl From<Post> for Note {
fn from (Post { id, content, .. }: Post) -> Note {
Note { id, content }
}
}
impl Activity {
/// Deliver the activity to all its targets through the ActivityPub
/// delivery mechanism.
pub async fn deliver <S> (self, signer: &S) -> Result<()>
where
S: sign::Sign + ?Sized,
{
// Create a shared client #efficiency
let client = reqwest::Client::new();
// the function that does the delivery to a target. It creates
// a request with the proper headers and signs it using the
// `signer`.
let do_delivery = |url| async {
let req = {
let mut r = client.get(url).build()?;
signer.sign(&mut r)?;
r
};
client
.execute(req)
.map_err(err)
.await
};
// Collect only the errors, since we don't need to do anything
// with a successful delivery.
let errors = self
.delivery_targets()
.await?
.into_iter()
.map(do_delivery)
.collect::<stream::FuturesUnordered<_>>()
.filter_map(|r: Result<_>| async {
r.err().map(err)
})
.collect::<Vec<Error>>()
.await;
for err in errors {
// Failure to deliver is not a fatal error per se,
// so we log and move on.
println!("Failed to deliver activity: {:?}", err);
}
Ok (())
}
/// Get all delivery targets as urls.
async fn delivery_targets (&self) -> Result<Vec<reqwest::Url>> {
let unId = |Id (x)| x;
Ok (self.to().chain(self.cc()).map(unId).collect())
}
/// Perform the activity.
pub async fn perform <S> (self, _: &mut Context<S>) -> Result<()>
where
S: sign::Sign
{
todo!()
}
}