implement a (very bad) derive macro for activityhandler

This commit is contained in:
Felix Ableitner 2021-07-07 03:29:31 +02:00
parent 3d2e19f3c3
commit 9af57acfe5
7 changed files with 168 additions and 37 deletions

47
Cargo.lock generated
View file

@ -1069,6 +1069,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
[[package]]
name = "dissimilar"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc4b29f4b9bb94bf267d57269fd0706d343a160937108e9619fe380645428abb"
[[package]] [[package]]
name = "either" name = "either"
version = "1.6.1" version = "1.6.1"
@ -1371,6 +1377,12 @@ version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce"
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.2.7" version = "0.2.7"
@ -1887,12 +1899,23 @@ dependencies = [
"activitystreams", "activitystreams",
"activitystreams-ext", "activitystreams-ext",
"async-trait", "async-trait",
"lemmy_apub_lib_derive",
"lemmy_utils", "lemmy_utils",
"lemmy_websocket", "lemmy_websocket",
"serde", "serde",
"url", "url",
] ]
[[package]]
name = "lemmy_apub_lib_derive"
version = "0.1.0"
dependencies = [
"proc-macro2 1.0.24",
"quote 1.0.8",
"syn 1.0.60",
"trybuild",
]
[[package]] [[package]]
name = "lemmy_apub_receive" name = "lemmy_apub_receive"
version = "0.1.0" version = "0.1.0"
@ -3652,6 +3675,15 @@ dependencies = [
"tokio 0.2.25", "tokio 0.2.25",
] ]
[[package]]
name = "toml"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "tower-service" name = "tower-service"
version = "0.3.1" version = "0.3.1"
@ -3735,6 +3767,21 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "trybuild"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1768998d9a3b179411618e377dbb134c58a88cda284b0aa71c42c40660127d46"
dependencies = [
"dissimilar",
"glob",
"lazy_static",
"serde",
"serde_json",
"termcolor",
"toml",
]
[[package]] [[package]]
name = "twoway" name = "twoway"
version = "0.2.1" version = "0.2.1"

View file

@ -15,6 +15,7 @@ members = [
"crates/api_crud", "crates/api_crud",
"crates/api_common", "crates/api_common",
"crates/apub_lib", "crates/apub_lib",
"crates/apub_lib_derive",
"crates/apub", "crates/apub",
"crates/apub_receive", "crates/apub_receive",
"crates/utils", "crates/utils",

View file

@ -6,6 +6,7 @@ edition = "2018"
[dependencies] [dependencies]
lemmy_utils = { path = "../utils" } lemmy_utils = { path = "../utils" }
lemmy_websocket = { path = "../websocket" } lemmy_websocket = { path = "../websocket" }
lemmy_apub_lib_derive = { path = "../apub_lib_derive" }
activitystreams = "0.7.0-alpha.11" activitystreams = "0.7.0-alpha.11"
activitystreams-ext = "0.1.0-alpha.2" activitystreams-ext = "0.1.0-alpha.2"
serde = { version = "1.0.123", features = ["derive"] } serde = { version = "1.0.123", features = ["derive"] }

View file

@ -4,6 +4,7 @@ use activitystreams::{
primitives::OneOrMany, primitives::OneOrMany,
unparsed::Unparsed, unparsed::Unparsed,
}; };
pub use lemmy_apub_lib_derive::*;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use std::marker::PhantomData; use std::marker::PhantomData;

View file

@ -0,0 +1,15 @@
[package]
name = "lemmy_apub_lib_derive"
version = "0.1.0"
edition = "2018"
[lib]
proc-macro = true
[dev-dependencies]
trybuild = { version = "1.0", features = ["diff"] }
[dependencies]
proc-macro2 = "1.0"
syn = "1.0"
quote = "1.0"

View file

@ -0,0 +1,102 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput};
#[proc_macro_derive(ActivityHandlerNew)]
pub fn derive_activity_handler(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
// Parse the input tokens into a syntax tree.
let input = parse_macro_input!(input as DeriveInput);
// Used in the quasi-quotation below as `#name`.
let name = input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let input_enum = if let Data::Enum(d) = input.data {
d
} else {
unimplemented!()
};
let impl_verify = input_enum
.variants
.iter()
.map(|variant| variant_impl_verify(&name, variant));
let impl_receive = input_enum
.variants
.iter()
.map(|variant| variant_impl_receive(&name, variant));
let impl_common = input_enum
.variants
.iter()
.map(|variant| variant_impl_common(&name, variant));
// The generated impl.
let expanded = quote! {
#[async_trait::async_trait(?Send)]
impl #impl_generics lemmy_apub_lib::ActivityHandlerNew for #name #ty_generics #where_clause {
async fn verify(
&self,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
match self {
#(#impl_verify)*
}
}
async fn receive(
&self,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
match self {
#(#impl_receive)*
}
}
fn common(&self) -> &ActivityCommonFields {
match self {
#(#impl_common)*
}
}
}
};
// Hand the output tokens back to the compiler.
proc_macro::TokenStream::from(expanded)
}
fn variant_impl_common(name: &syn::Ident, variant: &syn::Variant) -> TokenStream {
let id = &variant.ident;
match &variant.fields {
syn::Fields::Unnamed(_) => {
quote! {
#name::#id(a) => a.common(),
}
}
_ => unimplemented!(),
}
}
fn variant_impl_verify(name: &syn::Ident, variant: &syn::Variant) -> TokenStream {
let id = &variant.ident;
match &variant.fields {
syn::Fields::Unnamed(_) => {
quote! {
#name::#id(a) => a.verify(context, request_counter).await,
}
}
_ => unimplemented!(),
}
}
fn variant_impl_receive(name: &syn::Ident, variant: &syn::Variant) -> TokenStream {
let id = &variant.ident;
match &variant.fields {
syn::Fields::Unnamed(_) => {
quote! {
#name::#id(a) => a.receive(context, request_counter).await,
}
}
_ => unimplemented!(),
}
}

View file

@ -30,7 +30,7 @@ pub mod inbox_enums;
pub mod person; pub mod person;
pub mod post; pub mod post;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ActivityHandlerNew)]
#[serde(untagged)] #[serde(untagged)]
enum Ac { enum Ac {
CreatePost(CreatePost), CreatePost(CreatePost),
@ -38,42 +38,6 @@ enum Ac {
AcceptFollowCommunity(AcceptFollowCommunity), AcceptFollowCommunity(AcceptFollowCommunity),
} }
// TODO: write a derive trait which creates this
#[async_trait::async_trait(?Send)]
impl ActivityHandlerNew for Ac {
async fn verify(
&self,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
match self {
Ac::CreatePost(a) => a.verify(context, request_counter).await,
Ac::LikePost(a) => a.verify(context, request_counter).await,
Ac::AcceptFollowCommunity(a) => a.verify(context, request_counter).await,
}
}
async fn receive(
&self,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
match self {
Ac::CreatePost(a) => a.receive(context, request_counter).await,
Ac::LikePost(a) => a.receive(context, request_counter).await,
Ac::AcceptFollowCommunity(a) => a.receive(context, request_counter).await,
}
}
fn common(&self) -> &ActivityCommonFields {
match self {
Ac::CreatePost(a) => a.common(),
Ac::LikePost(a) => a.common(),
Ac::AcceptFollowCommunity(a) => a.common(),
}
}
}
pub async fn shared_inbox( pub async fn shared_inbox(
request: HttpRequest, request: HttpRequest,
mut body: web::Payload, mut body: web::Payload,