Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ tracing = { version = "0.1" }
either = "1"
void = "1"
indexmap = { version = "2.4.0", features = ["serde"] }
macro_utils = { path = "./tools/macro-utils" }

# ipfs dependency
rust-ipfs = "0.14.0"
Expand Down
12 changes: 12 additions & 0 deletions tools/macro-utils/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "macro_utils"
version = "0.1.0"
edition = "2021"

[dependencies]
syn = { version = "2.0.87", features = ["derive", "full"] }
quote = "1.0.37"
proc-macro2 = "1.0.89"

[lib]
proc-macro = true
51 changes: 51 additions & 0 deletions tools/macro-utils/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
extern crate proc_macro2;

use proc_macro::TokenStream;

mod struct_utils;

/// Implements a way to fetch all public implemented method for a given struct or trait.
/// It returns a tuple where the first element is the functions name and the second if its async
/// E.g.
/// ```
/// use macro_utils::impl_funcs;
///
/// pub struct A;
///
/// /// For structs
/// #[impl_funcs(name="get_all_impls")] /// Optional name parameter. If not present will be called functions
/// impl A {
/// fn some_method_a() {}
///
/// fn some_method_b() {}
///
/// async fn some_async_method_c() {}
/// }
/// assert_eq!([("some_method_a", false), ("some_method_b", false), ("some_async_method_c", true)], A::get_all_impls());
///
///
/// /// For traits
/// #[impl_funcs(name="get_all_impls")] /// Optional name parameter. If not present will be called functions
/// trait SomeTrait {
/// fn some_method_a();
///
/// fn some_method_b();
///
/// async fn some_async_method_c();
/// }
///
/// pub struct B;
/// impl SomeTrait for B {
/// fn some_method_a() {}
///
/// fn some_method_b() {}
///
/// async fn some_async_method_c() {}
/// }
///
/// assert_eq!([("some_method_a", false), ("some_method_b", false), ("some_async_method_c", true)], B::get_all_impls());
/// ```
#[proc_macro_attribute]
pub fn impl_funcs(attr: TokenStream, input: TokenStream) -> TokenStream {
struct_utils::expand(attr, input).unwrap_or_else(|e| e.into_compile_error().into())
}
97 changes: 97 additions & 0 deletions tools/macro-utils/src/struct_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{
parse::{Parse, Parser},
parse_quote,
punctuated::Punctuated,
spanned::Spanned,
Error, ImplItem, ItemImpl, ItemTrait, Meta, Result, TraitItem, Visibility,
};

pub fn expand(attr: TokenStream, input: TokenStream) -> Result<TokenStream> {
let attributes = Punctuated::<Meta, syn::Token![,]>::parse_terminated.parse(attr)?;
let mut name = "functions".into();
let expanded;
for attr in attributes {
if attr.path().is_ident("name") {
if let syn::Meta::NameValue(val) = &attr {
if let syn::Expr::Lit(v) = &val.value {
if let syn::Lit::Str(s) = &v.lit {
name = s.value();
}
}
}
} else {
return Err(Error::new(
attr.span(),
format!(
"Unrecognized attribute {:?}",
attr.path()
.get_ident()
.map(|i| i.to_string())
.unwrap_or_default()
),
));
}
}

if let Ok(mut impls) = ItemImpl::parse.parse(input.clone()) {
// Parse impl block
let mut functions = vec![];
for item in impls.items.iter() {
if let ImplItem::Fn(function) = item {
if !function
.attrs
.iter()
.any(|attr| attr.path().is_ident("skip"))
{
let is_async = function.sig.asyncness.is_some();
if matches!(function.vis, Visibility::Public(_) | Visibility::Inherited) {
let id = function.sig.ident.to_string();
functions.push(quote! {
(#id, #is_async)
});
}
}
}
}
let func = format_ident!("{name}");
impls.items.push(parse_quote! {
fn #func() -> &'static[(&'static str, bool)] {
&[ #(#functions),* ]
}
});
expanded = quote! {
#impls
};
} else {
// Parse trait block
let mut trait_impl: ItemTrait = ItemTrait::parse.parse(input)?;
let mut functions = vec![];
for item in &trait_impl.items {
if let TraitItem::Fn(function) = item {
if !function
.attrs
.iter()
.any(|attr| attr.path().is_ident("skip"))
{
let is_async = function.sig.asyncness.is_some();
let id = function.sig.ident.to_string();
functions.push(quote! {
(#id, #is_async)
});
}
}
}
let func = format_ident!("{name}");
trait_impl.items.push(parse_quote! {
fn #func() -> &'static[(&'static str, bool)] {
&[ #(#functions),* ]
}
});
expanded = quote! {
#trait_impl
};
}
Ok(TokenStream::from(expanded))
}
1 change: 1 addition & 0 deletions warp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ tracing = { workspace = true }
mediatype.workspace = true
send_wrapper.workspace = true
indexmap.workspace = true
macro_utils.workspace = true

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { workspace = true }
Expand Down
3 changes: 3 additions & 0 deletions warp/src/constellation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use chrono::{DateTime, Utc};
use directory::Directory;
use futures::stream::BoxStream;
use futures::Stream;
use macro_utils::impl_funcs;

#[derive(Debug, Clone)]
pub enum ConstellationEventKind {
Expand Down Expand Up @@ -82,6 +83,7 @@ pub type ConstellationProgressStream = BoxStream<'static, Progression>;

/// Interface that would provide functionality around the filesystem.
#[async_trait::async_trait]
#[impl_funcs(name = "constellation_impls")]
pub trait Constellation: ConstellationEvent + Extension + Sync + Send + SingleHandle {
/// Provides the timestamp of when the file system was modified
fn modified(&self) -> DateTime<Utc>;
Expand Down Expand Up @@ -218,6 +220,7 @@ pub trait Constellation: ConstellationEvent + Extension + Sync + Send + SingleHa
}

#[async_trait::async_trait]
#[impl_funcs(name = "constellation_event_impls")]
pub trait ConstellationEvent: Sync + Send {
/// Subscribe to an stream of events
async fn constellation_subscribe(&mut self) -> Result<ConstellationEventStream, Error> {
Expand Down
7 changes: 7 additions & 0 deletions warp/src/multipass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use chrono::{DateTime, Utc};
use futures::stream::BoxStream;
use futures::{Stream, StreamExt};
use macro_utils::impl_funcs;
use serde::{Deserialize, Serialize};
use std::future::Future;
use std::path::PathBuf;
Expand Down Expand Up @@ -69,6 +70,7 @@ pub enum IdentityImportOption<'a> {
pub type MultiPassEventStream = BoxStream<'static, MultiPassEventKind>;

#[async_trait::async_trait]
#[impl_funcs(name = "multipass_impls")]
pub trait MultiPass:
Extension
+ IdentityInformation
Expand All @@ -92,6 +94,7 @@ pub trait MultiPass:
}

#[async_trait::async_trait]
#[impl_funcs(name = "multipass_local_id_impls")]
pub trait LocalIdentity: Sync + Send {
/// Reference to the local [`Identity`]
async fn identity(&self) -> Result<Identity, Error>;
Expand All @@ -109,6 +112,7 @@ pub trait LocalIdentity: Sync + Send {
}

#[async_trait::async_trait]
#[impl_funcs(name = "multipass_im_export_impls")]
pub trait MultiPassImportExport: Sync + Send {
/// Import identity from a specific location
async fn import_identity<'a>(
Expand All @@ -125,6 +129,7 @@ pub trait MultiPassImportExport: Sync + Send {
}

#[async_trait::async_trait]
#[impl_funcs(name = "multipass_friends_impls")]
pub trait Friends: Sync + Send {
/// Send friend request to corresponding public key
async fn send_request(&mut self, _: &DID) -> Result<(), Error> {
Expand Down Expand Up @@ -203,6 +208,7 @@ pub trait Friends: Sync + Send {
}

#[async_trait::async_trait]
#[impl_funcs(name = "multipass_event_impls")]
pub trait MultiPassEvent: Sync + Send {
/// Subscribe to an stream of events
async fn multipass_subscribe(&mut self) -> Result<MultiPassEventStream, Error> {
Expand All @@ -211,6 +217,7 @@ pub trait MultiPassEvent: Sync + Send {
}

#[async_trait::async_trait]
#[impl_funcs(name = "multipass_identity_impls")]
pub trait IdentityInformation: Send + Sync {
/// Profile picture belonging to the `Identity`
async fn identity_picture(&self, _: &DID) -> Result<IdentityImage, Error> {
Expand Down
2 changes: 2 additions & 0 deletions warp/src/raygun/community.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use bytes::Bytes;
use chrono::{DateTime, Utc};
use futures::stream::BoxStream;
use indexmap::{IndexMap, IndexSet};
use macro_utils::impl_funcs;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

Expand Down Expand Up @@ -277,6 +278,7 @@ pub enum CommunityChannelPermission {
SendAttachments,
}

#[impl_funcs(name = "raygun_community_impls")]
#[async_trait::async_trait]
pub trait RayGunCommunity: Sync + Send {
async fn get_community_stream(
Expand Down
4 changes: 4 additions & 0 deletions warp/src/raygun/group.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![allow(clippy::result_large_err)]
use crate::crypto::DID;
use crate::error::Error;
use macro_utils::impl_funcs;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;
Expand Down Expand Up @@ -154,6 +155,7 @@ impl GroupInvitation {
}

// General/Base GroupChat Trait
#[impl_funcs(name = "raygun_group_chat_impls")]
pub trait GroupChat: GroupInvite + GroupChatManagement {
/// Join a existing group
fn join_group(&mut self, _: Uuid) -> Result<(), Error> {
Expand All @@ -172,6 +174,7 @@ pub trait GroupChat: GroupInvite + GroupChatManagement {
}

// Group Invite Management Trait
#[impl_funcs(name = "raygun_group_invite_impls")]
pub trait GroupInvite {
/// Sends a invite to join a group
fn send_invite(&mut self, _: Uuid, _: DID) -> Result<(), Error> {
Expand All @@ -195,6 +198,7 @@ pub trait GroupInvite {
}

// Group Admin Management Trait
#[impl_funcs(name = "raygun_group_management_impls")]
pub trait GroupChatManagement {
/// Create a group
fn create_group(&mut self, _: &str) -> Result<Group, Error> {
Expand Down
6 changes: 6 additions & 0 deletions warp/src/raygun/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use bytes::Bytes;
use chrono::{DateTime, Utc};
use core::ops::Range;
use indexmap::{IndexMap, IndexSet};
use macro_utils::impl_funcs;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeSet, HashSet};
use std::fmt::Debug;
Expand Down Expand Up @@ -1402,6 +1403,7 @@ impl PartialEq for Location {
impl Eq for Location {}

#[async_trait::async_trait]
#[impl_funcs(name = "raygun_impls")]
pub trait RayGun:
RayGunStream
+ RayGunGroupConversation
Expand Down Expand Up @@ -1589,6 +1591,7 @@ pub trait RayGunGroupConversation: Sync + Send {
}

#[async_trait::async_trait]
#[impl_funcs(name = "raygun_attachment_impls")]
pub trait RayGunAttachment: Sync + Send {
/// Send files to a conversation.
/// If no files is provided in the array, it will throw an error
Expand Down Expand Up @@ -1627,6 +1630,7 @@ pub trait RayGunAttachment: Sync + Send {
}

#[async_trait::async_trait]
#[impl_funcs(name = "raygun_stream_impls")]
pub trait RayGunStream: Sync + Send {
/// Subscribe to an stream of events from the conversation
async fn get_conversation_stream(&mut self, _: Uuid) -> Result<MessageEventStream, Error> {
Expand All @@ -1640,6 +1644,7 @@ pub trait RayGunStream: Sync + Send {
}

#[async_trait::async_trait]
#[impl_funcs(name = "raygun_events_impls")]
pub trait RayGunEvents: Sync + Send {
/// Send an event to a conversation
async fn send_event(&mut self, _: Uuid, _: MessageEvent) -> Result<(), Error> {
Expand All @@ -1653,6 +1658,7 @@ pub trait RayGunEvents: Sync + Send {
}

#[async_trait::async_trait]
#[impl_funcs(name = "raygun_conversations_impls")]
pub trait RayGunConversationInformation: Sync + Send {
/// Set a description to a conversation
async fn set_conversation_description(
Expand Down
Loading