Use enum_delegate crate (#5)

This commit is contained in:
Nutomic 2022-11-14 12:04:36 +00:00 committed by GitHub
parent 34a41d327f
commit da32da5a67
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 65 additions and 259 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
/target /target
/.idea

3
.idea/.gitignore vendored
View file

@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/derive/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/activitypub-federation-rust.iml" filepath="$PROJECT_DIR$/.idea/activitypub-federation-rust.iml" />
</modules>
</component>
</project>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

91
Cargo.lock generated
View file

@ -6,7 +6,6 @@ version = 3
name = "activitypub_federation" name = "activitypub_federation"
version = "0.2.3" version = "0.2.3"
dependencies = [ dependencies = [
"activitypub_federation_derive",
"activitystreams-kinds", "activitystreams-kinds",
"actix-rt", "actix-rt",
"actix-web", "actix-web",
@ -17,6 +16,7 @@ dependencies = [
"chrono", "chrono",
"derive_builder", "derive_builder",
"dyn-clone", "dyn-clone",
"enum_delegate",
"env_logger", "env_logger",
"http", "http",
"http-signature-normalization-actix", "http-signature-normalization-actix",
@ -36,16 +36,6 @@ dependencies = [
"url", "url",
] ]
[[package]]
name = "activitypub_federation_derive"
version = "0.2.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
"trybuild",
]
[[package]] [[package]]
name = "activitystreams-kinds" name = "activitystreams-kinds"
version = "0.2.1" version = "0.2.1"
@ -525,12 +515,6 @@ dependencies = [
"crypto-common", "crypto-common",
] ]
[[package]]
name = "dissimilar"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c97b9233581d84b8e1e689cdd3a47b6f69770084fc246e86a7f78b0d9c1d4a5"
[[package]] [[package]]
name = "dyn-clone" name = "dyn-clone"
version = "1.0.9" version = "1.0.9"
@ -552,6 +536,30 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "enum_delegate"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8ea75f31022cba043afe037940d73684327e915f88f62478e778c3de914cd0a"
dependencies = [
"enum_delegate_lib",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "enum_delegate_lib"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e1f6c3800b304a6be0012039e2a45a322a093539c45ab818d9e6895a39c90fe"
dependencies = [
"proc-macro2",
"quote",
"rand",
"syn",
]
[[package]] [[package]]
name = "env_logger" name = "env_logger"
version = "0.9.1" version = "0.9.1"
@ -716,12 +724,6 @@ dependencies = [
"wasi", "wasi",
] ]
[[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.3.14" version = "0.3.14"
@ -1572,15 +1574,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.37" version = "1.0.37"
@ -1691,15 +1684,6 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "toml"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "tower-service" name = "tower-service"
version = "0.3.2" version = "0.3.2"
@ -1776,22 +1760,6 @@ 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.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e13556ba7dba80b3c76d1331989a341290c77efcf688eca6c307ee3066383dd"
dependencies = [
"dissimilar",
"glob",
"once_cell",
"serde",
"serde_derive",
"serde_json",
"termcolor",
"toml",
]
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.15.0" version = "1.15.0"
@ -1970,15 +1938,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"

View file

@ -7,11 +7,7 @@ license = "AGPL-3.0"
repository = "https://github.com/LemmyNet/activitypub-federation-rust" repository = "https://github.com/LemmyNet/activitypub-federation-rust"
documentation = "https://docs.rs/activitypub_federation/" documentation = "https://docs.rs/activitypub_federation/"
[workspace]
members = ["derive"]
[dependencies] [dependencies]
activitypub_federation_derive = { version = "0.2.0", path = "derive" }
chrono = { version = "0.4.22", features = ["clock"], default-features = false } chrono = { version = "0.4.22", features = ["clock"], default-features = false }
serde = { version = "1.0.145", features = ["derive"] } serde = { version = "1.0.145", features = ["derive"] }
async-trait = "0.1.57" async-trait = "0.1.57"
@ -34,6 +30,7 @@ thiserror = "1.0.37"
derive_builder = "0.11.2" derive_builder = "0.11.2"
itertools = "0.10.5" itertools = "0.10.5"
dyn-clone = "1.0.9" dyn-clone = "1.0.9"
enum_delegate = "0.2.0"
[dev-dependencies] [dev-dependencies]
activitystreams-kinds = "0.2.1" activitystreams-kinds = "0.2.1"

View file

@ -1,18 +0,0 @@
[package]
name = "activitypub_federation_derive"
version = "0.2.0"
edition = "2021"
description = "High-level Activitypub framework"
license = "AGPL-3.0"
repository = "https://github.com/LemmyNet/activitypub-federation-rust"
[lib]
proc-macro = true
[dev-dependencies]
trybuild = { version = "1.0.63", features = ["diff"] }
[dependencies]
proc-macro2 = "1.0.39"
syn = "1.0.96"
quote = "1.0.18"

View file

@ -1,137 +0,0 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Fields::Unnamed, Ident, Variant};
/// Generates implementation ActivityHandler for an enum, which looks like the following (handling
/// all enum variants).
///
/// Based on this code:
/// ```ignore
/// #[derive(serde::Deserialize, serde::Serialize)]
/// #[serde(untagged)]
/// #[activity_handler(LemmyContext, LemmyError)]
/// pub enum PersonInboxActivities {
/// CreateNote(CreateNote),
/// UpdateNote(UpdateNote),
/// }
/// ```
/// It will generate this:
/// ```ignore
/// impl ActivityHandler for PersonInboxActivities {
/// type DataType = LemmyContext;
/// type Error = LemmyError;
///
/// async fn verify(
/// &self,
/// data: &Self::DataType,
/// request_counter: &mut i32,
/// ) -> Result<(), Self::Error> {
/// match self {
/// PersonInboxActivities::CreateNote(a) => a.verify(data, request_counter).await,
/// PersonInboxActivities::UpdateNote(a) => a.verify(context, request_counter).await,
/// }
/// }
///
/// async fn receive(
/// &self,
/// data: &Self::DataType,
/// request_counter: &mut i32,
/// ) -> Result<(), Self::Error> {
/// match self {
/// PersonInboxActivities::CreateNote(a) => a.receive(data, request_counter).await,
/// PersonInboxActivities::UpdateNote(a) => a.receive(data, request_counter).await,
/// }
/// }
/// ```
#[proc_macro_attribute]
pub fn activity_handler(
attr: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let derive_input = parse_macro_input!(input as DeriveInput);
let derive_input2 = derive_input.clone();
let attr = proc_macro2::TokenStream::from(attr);
let mut attr = attr.into_iter();
let data_type = attr.next().expect("data type input");
let _delimiter = attr.next();
let error = attr.next().expect("error type input");
let enum_name = derive_input2.ident;
let (impl_generics, ty_generics, where_clause) = derive_input2.generics.split_for_impl();
let enum_variants = if let Data::Enum(d) = derive_input2.data {
d.variants
} else {
unimplemented!()
};
let impl_id = enum_variants
.iter()
.map(|v| generate_match_arm(&enum_name, v, &quote! {a.id()}));
let impl_actor = enum_variants
.iter()
.map(|v| generate_match_arm(&enum_name, v, &quote! {a.actor()}));
let body_verify = quote! {a.verify(context, request_counter).await};
let impl_verify = enum_variants
.iter()
.map(|v| generate_match_arm(&enum_name, v, &body_verify));
let body_receive = quote! {a.receive(context, request_counter).await};
let impl_receive = enum_variants
.iter()
.map(|v| generate_match_arm(&enum_name, v, &body_receive));
let expanded = quote! {
#derive_input
#[async_trait::async_trait(?Send)]
impl #impl_generics activitypub_federation::traits::ActivityHandler for #enum_name #ty_generics #where_clause {
type DataType = #data_type;
type Error = #error;
fn id(
&self,
) -> &Url {
match self {
#(#impl_id)*
}
}
fn actor(
&self,
) -> &Url {
match self {
#(#impl_actor)*
}
}
async fn verify(
&self,
context: &activitypub_federation::data::Data<Self::DataType>,
request_counter: &mut i32,
) -> Result<(), Self::Error> {
match self {
#(#impl_verify)*
}
}
async fn receive(
self,
context: &activitypub_federation::data::Data<Self::DataType>,
request_counter: &mut i32,
) -> Result<(), Self::Error> {
match self {
#(#impl_receive)*
}
}
}
};
expanded.into()
}
fn generate_match_arm(enum_name: &Ident, variant: &Variant, body: &TokenStream) -> TokenStream {
let id = &variant.ident;
match &variant.fields {
Unnamed(_) => {
quote! {
#enum_name::#id(a) => #body,
}
}
_ => unimplemented!(),
}
}

View file

@ -11,11 +11,11 @@ use activitypub_federation::{
object_id::ObjectId, object_id::ObjectId,
signatures::{Keypair, PublicKey}, signatures::{Keypair, PublicKey},
}, },
data::Data,
deser::context::WithContext, deser::context::WithContext,
traits::{ActivityHandler, Actor, ApubObject}, traits::{ActivityHandler, Actor, ApubObject},
LocalInstance, LocalInstance,
}; };
use activitypub_federation_derive::activity_handler;
use activitystreams_kinds::actor::PersonType; use activitystreams_kinds::actor::PersonType;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
@ -33,9 +33,9 @@ pub struct MyUser {
} }
/// List of all activities which this actor can receive. /// List of all activities which this actor can receive.
#[activity_handler(InstanceHandle, Error)]
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug)]
#[serde(untagged)] #[serde(untagged)]
#[enum_delegate::implement(ActivityHandler)]
pub enum PersonAcceptedActivities { pub enum PersonAcceptedActivities {
Follow(Follow), Follow(Follow),
Accept(Accept), Accept(Accept),

View file

@ -1,10 +1,11 @@
use crate::data::Data; use crate::data::Data;
pub use activitypub_federation_derive::*;
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use std::ops::Deref;
use url::Url; use url::Url;
/// Trait which allows verification and reception of incoming activities. /// Trait which allows verification and reception of incoming activities.
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
#[enum_delegate::register]
pub trait ActivityHandler { pub trait ActivityHandler {
type DataType; type DataType;
type Error; type Error;
@ -32,6 +33,40 @@ pub trait ActivityHandler {
) -> Result<(), Self::Error>; ) -> Result<(), Self::Error>;
} }
/// Allow for boxing of enum variants
#[async_trait::async_trait(?Send)]
impl<T> ActivityHandler for Box<T>
where
T: ActivityHandler,
{
type DataType = T::DataType;
type Error = T::Error;
fn id(&self) -> &Url {
self.deref().id()
}
fn actor(&self) -> &Url {
self.deref().actor()
}
async fn verify(
&self,
data: &Data<Self::DataType>,
request_counter: &mut i32,
) -> Result<(), Self::Error> {
self.verify(data, request_counter).await
}
async fn receive(
self,
data: &Data<Self::DataType>,
request_counter: &mut i32,
) -> Result<(), Self::Error> {
self.receive(data, request_counter).await
}
}
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
pub trait ApubObject { pub trait ApubObject {
type DataType; type DataType;