mainsail 0.1.0

Batteries-included ActivityPub Library
Documentation
use std::ops::Deref;

use hyper::body::Bytes;
use http::Request;
use serde::Deserialize;
use tracing::warn;

use crate::activitypub::objects::actor::Actor;
use crate::activitypub::signature::validate_activitypub_request;
use crate::error::{bad_request, MainsailError};
use crate::local::InternalResult;
use crate::database::Database;

use crate::get_server_internal_actor;

pub struct NoAuth;

pub type UnverifiedApRequest = ApRequest<NoAuth>;
pub type AuthedApRequest = ApRequest<Actor>;

pub struct ApRequest<T> {
    pub request: Request<Bytes>,
    pub db: Database,
    pub domain: String,
    pub actor: T,
}

pub async fn new_ap_request(
    request: Request<Bytes>,
    db: Database,
    domain: String,
) -> Result<UnverifiedApRequest, MainsailError> {
    Ok(ApRequest { request, db, domain, actor: NoAuth })
}

impl<T> ApRequest<T> {
    pub fn get_url_param(&self, pos: usize, message: &str) -> Result<&str, MainsailError> {
        self.uri().path().split('/').nth(pos)
            .ok_or(bad_request(message))
    }

    pub fn get_int_url_param(&self, pos: usize, message: &str) -> Result<i64, MainsailError> {
        let str_param = self.get_url_param(pos, message)?;
        let param = str_param.parse::<i64>();
        match param {
            Ok(p) => Ok(p),
            Err(_) => {
                warn!("Failed to parse parameter {} as integer in URL {}", str_param, self.uri());
                Err(bad_request(message))
            }
        }
    }

    pub fn get_trailing_param(&self, message: &str) -> Result<&str, MainsailError> {
        self.uri()
            .path()
            .split('/')
            .next_back()
            .ok_or(bad_request(message))
    }

    pub fn json<'a, V: Deserialize<'a>>(&'a self) -> InternalResult<V> {
        serde_json::from_slice::<V>(self.body()).map_err(|_| {
            bad_request("Sorry, we were unable to parse that request. \
                         Please check that it contains all the required fields."
            )
        })
    }

    pub fn text(&self) -> Result<String, MainsailError> {
        String::from_utf8(self.body().to_vec()).map_err(|_| { MainsailError::BodyNotUTF8 })
    }
}

impl ApRequest<NoAuth> {
    pub async fn authenticate(self) -> InternalResult<AuthedApRequest> {
        let server = get_server_internal_actor(&self.db)?;
        let actor = validate_activitypub_request(&self, &server).await?;

        let request = self.request;
        let db = self.db;
        let domain = self.domain;
        let data = actor;

        Ok(ApRequest { request, db, domain, actor: data })
    }
}

impl<T> Deref for ApRequest<T> {
    type Target = Request<Bytes>;
    fn deref(&self) -> &Self::Target {
        &self.request
    }
}