proc macro

Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
Jason Volk 2024-07-24 00:14:03 +00:00
parent 2468e0c3de
commit 85f734ec74
10 changed files with 123 additions and 70 deletions

11
Cargo.lock generated
View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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)]

View file

@ -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?,
})
}

View file

@ -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)

View file

@ -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
View 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
View 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
View 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)
}