proc macro ✨
Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
parent
2468e0c3de
commit
85f734ec74
10 changed files with 123 additions and 70 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -612,6 +612,7 @@ dependencies = [
|
|||
"clap",
|
||||
"conduit_api",
|
||||
"conduit_core",
|
||||
"conduit_macros",
|
||||
"conduit_service",
|
||||
"const-str",
|
||||
"futures-util",
|
||||
|
@ -712,6 +713,16 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "conduit_macros"
|
||||
version = "0.4.5"
|
||||
dependencies = [
|
||||
"conduit_core",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "conduit_router"
|
||||
version = "0.4.6"
|
||||
|
|
15
Cargo.toml
15
Cargo.toml
|
@ -413,6 +413,16 @@ default-features = false
|
|||
[workspace.dependencies.checked_ops]
|
||||
version = "0.1"
|
||||
|
||||
[workspace.dependencies.syn]
|
||||
version = "1.0"
|
||||
features = ["full", "extra-traits"]
|
||||
|
||||
[workspace.dependencies.quote]
|
||||
version = "1.0"
|
||||
|
||||
[workspace.dependencies.proc-macro2]
|
||||
version = "1.0.86"
|
||||
|
||||
|
||||
#
|
||||
# Patches
|
||||
|
@ -480,6 +490,11 @@ package = "conduit_core"
|
|||
path = "src/core"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.conduit-macros]
|
||||
package = "conduit_macros"
|
||||
path = "src/macros"
|
||||
default-features = false
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Release profiles
|
||||
|
|
|
@ -29,6 +29,7 @@ release_max_log_level = [
|
|||
clap.workspace = true
|
||||
conduit-api.workspace = true
|
||||
conduit-core.workspace = true
|
||||
conduit-macros.workspace = true
|
||||
conduit-service.workspace = true
|
||||
const-str.workspace = true
|
||||
futures-util.workspace = true
|
||||
|
|
|
@ -477,7 +477,7 @@ pub(super) async fn latest_pdu_in_room(_body: Vec<&str>, room_id: Box<RoomId>) -
|
|||
|
||||
#[tracing::instrument(skip(_body))]
|
||||
pub(super) async fn force_set_room_state_from_server(
|
||||
_body: Vec<&str>, server_name: Box<ServerName>, room_id: Box<RoomId>,
|
||||
_body: Vec<&str>, room_id: Box<RoomId>, server_name: Box<ServerName>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
if !services()
|
||||
.rooms
|
||||
|
@ -691,18 +691,19 @@ pub(super) async fn resolve_true_destination(
|
|||
Ok(RoomMessageEventContent::text_markdown(msg))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(super) fn memory_stats() -> RoomMessageEventContent {
|
||||
pub(super) async fn memory_stats(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let html_body = conduit::alloc::memory_stats();
|
||||
|
||||
if html_body.is_none() {
|
||||
return RoomMessageEventContent::text_plain("malloc stats are not supported on your compiled malloc.");
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"malloc stats are not supported on your compiled malloc.",
|
||||
));
|
||||
}
|
||||
|
||||
RoomMessageEventContent::text_html(
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
"This command's output can only be viewed by clients that render HTML.".to_owned(),
|
||||
html_body.expect("string result"),
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(tokio_unstable)]
|
||||
|
|
|
@ -3,11 +3,12 @@ pub(crate) mod tester;
|
|||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use conduit_macros::admin_command_dispatch;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, EventId, OwnedRoomOrAliasId, RoomId, ServerName};
|
||||
use tester::TesterCommand;
|
||||
|
||||
use self::commands::*;
|
||||
use self::{commands::*, tester::TesterCommand};
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub(super) enum DebugCommand {
|
||||
/// - Echo input of admin command
|
||||
|
@ -176,63 +177,6 @@ pub(super) enum DebugCommand {
|
|||
|
||||
/// - Developer test stubs
|
||||
#[command(subcommand)]
|
||||
#[allow(non_snake_case)]
|
||||
Tester(TesterCommand),
|
||||
}
|
||||
|
||||
pub(super) async fn process(command: DebugCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(match command {
|
||||
DebugCommand::Echo {
|
||||
message,
|
||||
} => echo(body, message).await?,
|
||||
DebugCommand::GetSigningKeys {
|
||||
server_name,
|
||||
cached,
|
||||
} => get_signing_keys(body, server_name, cached).await?,
|
||||
DebugCommand::GetAuthChain {
|
||||
event_id,
|
||||
} => get_auth_chain(body, event_id).await?,
|
||||
DebugCommand::ParsePdu => parse_pdu(body).await?,
|
||||
DebugCommand::GetPdu {
|
||||
event_id,
|
||||
} => get_pdu(body, event_id).await?,
|
||||
DebugCommand::GetRemotePdu {
|
||||
event_id,
|
||||
server,
|
||||
} => get_remote_pdu(body, event_id, server).await?,
|
||||
DebugCommand::GetRoomState {
|
||||
room_id,
|
||||
} => get_room_state(body, room_id).await?,
|
||||
DebugCommand::Ping {
|
||||
server,
|
||||
} => ping(body, server).await?,
|
||||
DebugCommand::ForceDeviceListUpdates => force_device_list_updates(body).await?,
|
||||
DebugCommand::ChangeLogLevel {
|
||||
filter,
|
||||
reset,
|
||||
} => change_log_level(body, filter, reset).await?,
|
||||
DebugCommand::SignJson => sign_json(body).await?,
|
||||
DebugCommand::VerifyJson => verify_json(body).await?,
|
||||
DebugCommand::FirstPduInRoom {
|
||||
room_id,
|
||||
} => first_pdu_in_room(body, room_id).await?,
|
||||
DebugCommand::LatestPduInRoom {
|
||||
room_id,
|
||||
} => latest_pdu_in_room(body, room_id).await?,
|
||||
DebugCommand::GetRemotePduList {
|
||||
server,
|
||||
force,
|
||||
} => get_remote_pdu_list(body, server, force).await?,
|
||||
DebugCommand::ForceSetRoomStateFromServer {
|
||||
room_id,
|
||||
server_name,
|
||||
} => force_set_room_state_from_server(body, server_name, room_id).await?,
|
||||
DebugCommand::ResolveTrueDestination {
|
||||
server_name,
|
||||
no_cache,
|
||||
} => resolve_true_destination(body, server_name, no_cache).await?,
|
||||
DebugCommand::MemoryStats => memory_stats(),
|
||||
DebugCommand::RuntimeMetrics => runtime_metrics(body).await?,
|
||||
DebugCommand::RuntimeInterval => runtime_interval(body).await?,
|
||||
DebugCommand::Tester(command) => tester::process(command, body).await?,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::{panic::AssertUnwindSafe, time::Instant};
|
||||
|
||||
use clap::{CommandFactory, Parser};
|
||||
use conduit::{error, trace, Error};
|
||||
use conduit::{error, trace, utils::string::common_prefix, Error, Result};
|
||||
use futures_util::future::FutureExt;
|
||||
use ruma::{
|
||||
events::{
|
||||
|
@ -10,8 +10,6 @@ use ruma::{
|
|||
},
|
||||
OwnedEventId,
|
||||
};
|
||||
|
||||
use conduit::{utils::string::common_prefix, Result};
|
||||
use service::{
|
||||
admin::{Command, CommandOutput, CommandResult, HandlerResult},
|
||||
Services,
|
||||
|
@ -36,7 +34,7 @@ pub(super) fn handle(command: Command) -> HandlerResult { Box::pin(handle_comman
|
|||
|
||||
#[tracing::instrument(skip_all, name = "admin")]
|
||||
async fn handle_command(command: Command) -> CommandResult {
|
||||
AssertUnwindSafe(process_command(&command))
|
||||
AssertUnwindSafe(Box::pin(process_command(&command)))
|
||||
.catch_unwind()
|
||||
.await
|
||||
.map_err(Error::from_panic)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#![recursion_limit = "168"]
|
||||
#![allow(clippy::wildcard_imports)]
|
||||
#![allow(clippy::enum_glob_use)]
|
||||
|
||||
pub(crate) mod admin;
|
||||
pub(crate) mod handler;
|
||||
|
|
24
src/macros/Cargo.toml
Normal file
24
src/macros/Cargo.toml
Normal file
|
@ -0,0 +1,24 @@
|
|||
[package]
|
||||
name = "conduit_macros"
|
||||
categories.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "conduit_macros"
|
||||
path = "mod.rs"
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn.workspace = true
|
||||
quote.workspace = true
|
||||
proc-macro2.workspace = true
|
||||
conduit-core.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
50
src/macros/admin.rs
Normal file
50
src/macros/admin.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
use conduit_core::utils::string::camel_to_snake_string;
|
||||
use proc_macro::{Span, TokenStream};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, AttributeArgs, Fields, Ident, ItemEnum, Variant};
|
||||
|
||||
pub(super) fn command_dispatch(args: TokenStream, input_: TokenStream) -> TokenStream {
|
||||
let input = input_.clone();
|
||||
let item = parse_macro_input!(input as ItemEnum);
|
||||
let _args = parse_macro_input!(args as AttributeArgs);
|
||||
let arm = item.variants.iter().map(dispatch_arm);
|
||||
let name = item.ident;
|
||||
let q = quote! {
|
||||
pub(super) async fn process(command: #name, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
use #name::*;
|
||||
#[allow(non_snake_case)]
|
||||
Ok(match command {
|
||||
#( #arm )*
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
[input_, q.into()].into_iter().collect::<TokenStream>()
|
||||
}
|
||||
|
||||
fn dispatch_arm(v: &Variant) -> TokenStream2 {
|
||||
let name = &v.ident;
|
||||
let target = camel_to_snake_string(&format!("{name}"));
|
||||
let handler = Ident::new(&target, Span::call_site().into());
|
||||
match &v.fields {
|
||||
Fields::Named(fields) => {
|
||||
let field = fields.named.iter().filter_map(|f| f.ident.as_ref());
|
||||
let arg = field.clone();
|
||||
quote! {
|
||||
#name { #( #field ),* } => Box::pin(#handler(body, #( #arg ),*)).await?,
|
||||
}
|
||||
},
|
||||
Fields::Unnamed(fields) => {
|
||||
let field = &fields.unnamed.first().expect("one field");
|
||||
quote! {
|
||||
#name ( #field ) => Box::pin(#handler::process(#field, body)).await?,
|
||||
}
|
||||
},
|
||||
Fields::Unit => {
|
||||
quote! {
|
||||
#name => Box::pin(#handler(body)).await?,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
8
src/macros/mod.rs
Normal file
8
src/macros/mod.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
mod admin;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn admin_command_dispatch(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
admin::command_dispatch(args, input)
|
||||
}
|
Loading…
Add table
Reference in a new issue