More macros

This commit is contained in:
Riley Apeldoorn 2024-05-02 23:03:31 +02:00
parent edc21b4403
commit 21b47409f1
3 changed files with 63 additions and 32 deletions

View file

@ -29,6 +29,46 @@ macro_rules! fuck {
};
}
// Makes a response.
macro_rules! respond {
($($arg:tt)*) => {
crate::api::Resp {
$($arg)*,
.. crate::api::Resp::default()
}.into()
};
}
/// Parameters for a response
struct Resp<'s> {
body: Option<&'s Value>,
kind: &'s str,
code: u16,
}
impl<'s> Default for Resp<'s> {
fn default() -> Self {
Resp {
body: None,
kind: "application/json",
code: 200,
}
}
}
impl<'a> From<Resp<'a>> for Response {
fn from(Resp { body, kind, code }: Resp<'_>) -> Response {
let resp = Response::<()>::builder()
.status(code)
.header("content-type", kind);
resp.body(match body {
Some(data) => Full::new(serde_json::to_vec(&data).unwrap().into()),
None => Full::new(Bytes::default()),
})
.unwrap()
}
}
pub mod ap;
pub mod wf;
@ -205,23 +245,6 @@ fn with_json(
}
}
/// A quick, simple way to construct a response.
fn respond<const N: usize>(
status: u16,
body: Option<impl Into<Bytes>>,
headers: [(&str, &str); N],
) -> Response {
let mut resp = Response::<()>::builder().status(status);
for (name, data) in headers {
resp = resp.header(name, data);
}
resp.body(match body {
Some(bytes) => Full::new(bytes.into()),
None => Full::new(Bytes::default()),
})
.unwrap()
}
mod error {
//! Pre-baked error responses.
@ -261,10 +284,13 @@ mod error {
}
/// Check whether the requester wants json from us.
pub fn accepts_json(&self) -> bool {
fn is_json((k, v): (&str, &str)) -> bool {
k == "application" && v.split('+').any(|p| p == "json")
}
self.accept
.iter()
.filter_map(|s| s.split_once('/'))
.any(|(k, v)| k == "application" && v.split('+').any(|p| p == "json"))
.any(is_json)
}
}
}

View file

@ -6,19 +6,19 @@ use puppy::{
actor::{get_signing_key, Actor},
get_local_ap_object, Context, Error, Key,
};
use serde_json::{to_string, Value};
use serde_json::Value;
use crate::sig::{Signer, Verifier};
use super::{error::Message, respond, Response};
use super::{error::Message, Response};
/// Proxy a request through the instance.
pub async fn proxy(cx: &Context, params: &[(&str, &str)]) -> Result<Response, Message> {
// Extract our query parameters.
let Some(user) = params.iter().find_map(|(k, v)| (*k == "user").then_some(v)) else {
fuck!(400: "Expected `user` query param");
fuck!(400: "expected `user` query param");
};
let Some(url) = params.iter().find_map(|(k, v)| (*k == "url").then_some(v)) else {
fuck!(400: "Expected `url` query param");
fuck!(400: "expected `url` query param");
};
// Look up the actor's key in the store (which is accessible through the puppy context).
@ -48,19 +48,22 @@ pub fn serve_object(cx: &Context, object_ulid: &str) -> Result<Response, Message
let Ok(parsed) = object_ulid.parse::<Key>() else {
fuck!(400: "improperly formatted ulid");
};
let result = cx.run(|tx| get_local_ap_object(&tx, parsed));
let Ok(object) = result else {
fuck!(404: "object does not exist");
};
let json = to_string(&object.to_json_ld()).unwrap();
Ok(respond(200, Some(json), [AP_CONTENT_TYPE]))
}
const AP_CONTENT_TYPE: (&str, &str) = ("content-type", "application/activity+json");
Ok(respond! {
kind: "application/activity+json",
body: Some(&object.to_json_ld())
})
}
/// Serve the special actor used for signing requests.
pub fn serve_verifier_actor(verifier: &Verifier) -> Response {
let body = verifier.to_json_ld();
let encoded = serde_json::to_vec(&body).unwrap();
respond(200, Some(encoded), [AP_CONTENT_TYPE])
respond! {
kind: "application/activity+json",
body: Some(&verifier.to_json_ld())
}
}

View file

@ -7,7 +7,7 @@ use puppy::{
use serde_json::{json, Value};
use derive_more::Display;
use super::{error::Message, respond, Response};
use super::{error::Message, Response};
const WF_CONTENT_TYPE: (&str, &str) = ("content-type", "application/jrd+json");
@ -23,8 +23,10 @@ pub fn resolve(cx: &Context, params: &[(&str, &str)]) -> Result<Response, Messag
fuck!(500: "internal error");
};
let jrd = make_jrd(handle, &id);
let encoded = serde_json::to_vec(&jrd).unwrap();
Ok(respond(200, Some(encoded), [WF_CONTENT_TYPE]))
Ok(respond! {
body: Some(&jrd),
kind: "application/jrd+json"
})
}
Some(_) | None => fuck!(400: "missing/invalid resource param"),
}