upgrade to syn 2.x
Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
parent
3b5607ecdc
commit
ca82b59c6f
11 changed files with 138 additions and 99 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -731,9 +731,10 @@ dependencies = [
|
||||||
name = "conduit_macros"
|
name = "conduit_macros"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"itertools 0.13.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 1.0.109",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -426,7 +426,7 @@ default-features = false
|
||||||
version = "0.1"
|
version = "0.1"
|
||||||
|
|
||||||
[workspace.dependencies.syn]
|
[workspace.dependencies.syn]
|
||||||
version = "1.0"
|
version = "2.0"
|
||||||
features = ["full", "extra-traits"]
|
features = ["full", "extra-traits"]
|
||||||
|
|
||||||
[workspace.dependencies.quote]
|
[workspace.dependencies.quote]
|
||||||
|
|
|
@ -16,19 +16,19 @@ use crate::Result;
|
||||||
|
|
||||||
#[cargo_manifest]
|
#[cargo_manifest]
|
||||||
const WORKSPACE_MANIFEST: &'static str = ();
|
const WORKSPACE_MANIFEST: &'static str = ();
|
||||||
#[cargo_manifest("macros")]
|
#[cargo_manifest(crate = "macros")]
|
||||||
const MACROS_MANIFEST: &'static str = ();
|
const MACROS_MANIFEST: &'static str = ();
|
||||||
#[cargo_manifest("core")]
|
#[cargo_manifest(crate = "core")]
|
||||||
const CORE_MANIFEST: &'static str = ();
|
const CORE_MANIFEST: &'static str = ();
|
||||||
#[cargo_manifest("database")]
|
#[cargo_manifest(crate = "database")]
|
||||||
const DATABASE_MANIFEST: &'static str = ();
|
const DATABASE_MANIFEST: &'static str = ();
|
||||||
#[cargo_manifest("service")]
|
#[cargo_manifest(crate = "service")]
|
||||||
const SERVICE_MANIFEST: &'static str = ();
|
const SERVICE_MANIFEST: &'static str = ();
|
||||||
#[cargo_manifest("admin")]
|
#[cargo_manifest(crate = "admin")]
|
||||||
const ADMIN_MANIFEST: &'static str = ();
|
const ADMIN_MANIFEST: &'static str = ();
|
||||||
#[cargo_manifest("router")]
|
#[cargo_manifest(crate = "router")]
|
||||||
const ROUTER_MANIFEST: &'static str = ();
|
const ROUTER_MANIFEST: &'static str = ();
|
||||||
#[cargo_manifest("main")]
|
#[cargo_manifest(crate = "main")]
|
||||||
const MAIN_MANIFEST: &'static str = ();
|
const MAIN_MANIFEST: &'static str = ();
|
||||||
|
|
||||||
/// Processed list of features access all project crates. This is generated from
|
/// Processed list of features access all project crates. This is generated from
|
||||||
|
|
|
@ -18,6 +18,7 @@ proc-macro = true
|
||||||
syn.workspace = true
|
syn.workspace = true
|
||||||
quote.workspace = true
|
quote.workspace = true
|
||||||
proc-macro2.workspace = true
|
proc-macro2.workspace = true
|
||||||
|
itertools.workspace = true
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
|
use itertools::Itertools;
|
||||||
use proc_macro::{Span, TokenStream};
|
use proc_macro::{Span, TokenStream};
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::quote;
|
use quote::{quote, ToTokens};
|
||||||
use syn::{parse_macro_input, AttributeArgs, Fields, Ident, ItemEnum, Variant};
|
use syn::{Error, Fields, Ident, ItemEnum, Meta, Variant};
|
||||||
|
|
||||||
use crate::utils::camel_to_snake_string;
|
use crate::{utils::camel_to_snake_string, Result};
|
||||||
|
|
||||||
pub(super) fn command_dispatch(args: TokenStream, input_: TokenStream) -> TokenStream {
|
pub(super) fn command_dispatch(item: ItemEnum, _args: &[Meta]) -> Result<TokenStream> {
|
||||||
let input = input_.clone();
|
let name = &item.ident;
|
||||||
let item = parse_macro_input!(input as ItemEnum);
|
let arm: Vec<TokenStream2> = item.variants.iter().map(dispatch_arm).try_collect()?;
|
||||||
let _args = parse_macro_input!(args as AttributeArgs);
|
let switch = quote! {
|
||||||
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> {
|
pub(super) async fn process(command: #name, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||||
use #name::*;
|
use #name::*;
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
@ -21,14 +19,17 @@ pub(super) fn command_dispatch(args: TokenStream, input_: TokenStream) -> TokenS
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
[input_, q.into()].into_iter().collect::<TokenStream>()
|
Ok([item.into_token_stream(), switch]
|
||||||
|
.into_iter()
|
||||||
|
.collect::<TokenStream2>()
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch_arm(v: &Variant) -> TokenStream2 {
|
fn dispatch_arm(v: &Variant) -> Result<TokenStream2> {
|
||||||
let name = &v.ident;
|
let name = &v.ident;
|
||||||
let target = camel_to_snake_string(&format!("{name}"));
|
let target = camel_to_snake_string(&format!("{name}"));
|
||||||
let handler = Ident::new(&target, Span::call_site().into());
|
let handler = Ident::new(&target, Span::call_site().into());
|
||||||
match &v.fields {
|
let res = match &v.fields {
|
||||||
Fields::Named(fields) => {
|
Fields::Named(fields) => {
|
||||||
let field = fields.named.iter().filter_map(|f| f.ident.as_ref());
|
let field = fields.named.iter().filter_map(|f| f.ident.as_ref());
|
||||||
let arg = field.clone();
|
let arg = field.clone();
|
||||||
|
@ -37,7 +38,9 @@ fn dispatch_arm(v: &Variant) -> TokenStream2 {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Fields::Unnamed(fields) => {
|
Fields::Unnamed(fields) => {
|
||||||
let field = &fields.unnamed.first().expect("one field");
|
let Some(ref field) = fields.unnamed.first() else {
|
||||||
|
return Err(Error::new(Span::call_site().into(), "One unnamed field required"));
|
||||||
|
};
|
||||||
quote! {
|
quote! {
|
||||||
#name ( #field ) => Box::pin(#handler::process(#field, body)).await?,
|
#name ( #field ) => Box::pin(#handler::process(#field, body)).await?,
|
||||||
}
|
}
|
||||||
|
@ -47,5 +50,7 @@ fn dispatch_arm(v: &Variant) -> TokenStream2 {
|
||||||
#name => Box::pin(#handler(&body)).await?,
|
#name => Box::pin(#handler(&body)).await?,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1,34 @@
|
||||||
use std::{fs::read_to_string, path::PathBuf};
|
use std::{fs::read_to_string, path::PathBuf};
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::{Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{parse_macro_input, AttributeArgs, ItemConst, Lit, NestedMeta};
|
use syn::{Error, ItemConst, Meta};
|
||||||
|
|
||||||
pub(super) fn manifest(args: TokenStream, item: TokenStream) -> TokenStream {
|
use crate::{utils, Result};
|
||||||
let item = parse_macro_input!(item as ItemConst);
|
|
||||||
let args = parse_macro_input!(args as AttributeArgs);
|
|
||||||
let member = args.into_iter().find_map(|arg| {
|
|
||||||
let NestedMeta::Lit(arg) = arg else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let Lit::Str(arg) = arg else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
Some(arg.value())
|
|
||||||
});
|
|
||||||
|
|
||||||
let path = manifest_path(member.as_deref());
|
pub(super) fn manifest(item: ItemConst, args: &[Meta]) -> Result<TokenStream> {
|
||||||
|
let member = utils::get_named_string(args, "crate");
|
||||||
|
let path = manifest_path(member.as_deref())?;
|
||||||
let manifest = read_to_string(&path).unwrap_or_default();
|
let manifest = read_to_string(&path).unwrap_or_default();
|
||||||
|
|
||||||
let name = item.ident;
|
|
||||||
let val = manifest.as_str();
|
let val = manifest.as_str();
|
||||||
|
let name = item.ident;
|
||||||
let ret = quote! {
|
let ret = quote! {
|
||||||
const #name: &'static str = #val;
|
const #name: &'static str = #val;
|
||||||
};
|
};
|
||||||
|
|
||||||
ret.into()
|
Ok(ret.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::option_env_unwrap)]
|
#[allow(clippy::option_env_unwrap)]
|
||||||
fn manifest_path(member: Option<&str>) -> PathBuf {
|
fn manifest_path(member: Option<&str>) -> Result<PathBuf> {
|
||||||
let mut path: PathBuf = option_env!("CARGO_MANIFEST_DIR")
|
let Some(path) = option_env!("CARGO_MANIFEST_DIR") else {
|
||||||
.expect("missing CARGO_MANIFEST_DIR in environment")
|
return Err(Error::new(
|
||||||
.into();
|
Span::call_site().into(),
|
||||||
|
"missing CARGO_MANIFEST_DIR in environment",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut path: PathBuf = path.into();
|
||||||
|
|
||||||
// conduwuit/src/macros/ -> conduwuit/src/
|
// conduwuit/src/macros/ -> conduwuit/src/
|
||||||
path.pop();
|
path.pop();
|
||||||
|
@ -47,5 +42,6 @@ fn manifest_path(member: Option<&str>) -> PathBuf {
|
||||||
}
|
}
|
||||||
|
|
||||||
path.push("Cargo.toml");
|
path.push("Cargo.toml");
|
||||||
path
|
|
||||||
|
Ok(path)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use syn::{parse_macro_input, AttributeArgs, Item};
|
use quote::ToTokens;
|
||||||
|
use syn::{Item, Meta};
|
||||||
|
|
||||||
pub(super) fn recursion_depth(args: TokenStream, item_: TokenStream) -> TokenStream {
|
use crate::Result;
|
||||||
let item = item_.clone();
|
|
||||||
let item = parse_macro_input!(item as Item);
|
|
||||||
let _args = parse_macro_input!(args as AttributeArgs);
|
|
||||||
|
|
||||||
|
pub(super) fn recursion_depth(item: Item, _args: &[Meta]) -> Result<TokenStream> {
|
||||||
let mut best: usize = 0;
|
let mut best: usize = 0;
|
||||||
let mut count: usize = 0;
|
let mut count: usize = 0;
|
||||||
// think you'd find a fancy recursive ast visitor? think again
|
// think you'd find a fancy recursive ast visitor? think again
|
||||||
|
@ -24,5 +23,5 @@ pub(super) fn recursion_depth(args: TokenStream, item_: TokenStream) -> TokenStr
|
||||||
println!("DEPTH: {best}");
|
println!("DEPTH: {best}");
|
||||||
println!("LENGTH: {count}");
|
println!("LENGTH: {count}");
|
||||||
|
|
||||||
item_
|
Ok(item.into_token_stream().into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,26 @@
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::{quote, ToTokens};
|
use quote::quote;
|
||||||
use syn::{parse_macro_input, AttributeArgs, ItemFn, Meta, NestedMeta};
|
use syn::{ItemFn, Meta, MetaList};
|
||||||
|
|
||||||
pub(super) fn implement(args: TokenStream, input: TokenStream) -> TokenStream {
|
use crate::Result;
|
||||||
let args = parse_macro_input!(args as AttributeArgs);
|
|
||||||
let item = parse_macro_input!(input as ItemFn);
|
|
||||||
|
|
||||||
let NestedMeta::Meta(Meta::Path(receiver)) = args
|
pub(super) fn implement(item: ItemFn, args: &[Meta]) -> Result<TokenStream> {
|
||||||
|
let Meta::List(MetaList {
|
||||||
|
path,
|
||||||
|
..
|
||||||
|
}) = &args
|
||||||
.first()
|
.first()
|
||||||
.expect("missing path to trait or item to implement")
|
.expect("missing path to trait or item to implement")
|
||||||
else {
|
else {
|
||||||
panic!("invalid path to item for implement");
|
panic!("invalid path to item for implement");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let input = item;
|
||||||
let out = quote! {
|
let out = quote! {
|
||||||
impl #receiver {
|
impl #path {
|
||||||
#item
|
#input
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
out.into_token_stream().into()
|
Ok(out.into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,23 +7,50 @@ mod rustc;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
|
use syn::{
|
||||||
|
parse::{Parse, Parser},
|
||||||
|
parse_macro_input, Error, Item, ItemConst, ItemEnum, ItemFn, Meta,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn admin_command_dispatch(args: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn admin_command_dispatch(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
admin::command_dispatch(args, input)
|
attribute_macro::<ItemEnum, _>(args, input, admin::command_dispatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn cargo_manifest(args: TokenStream, input: TokenStream) -> TokenStream { cargo::manifest(args, input) }
|
pub fn cargo_manifest(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
attribute_macro::<ItemConst, _>(args, input, cargo::manifest)
|
||||||
|
}
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn recursion_depth(args: TokenStream, input: TokenStream) -> TokenStream { debug::recursion_depth(args, input) }
|
pub fn recursion_depth(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
attribute_macro::<Item, _>(args, input, debug::recursion_depth)
|
||||||
|
}
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn rustc_flags_capture(args: TokenStream) -> TokenStream { rustc::flags_capture(args) }
|
pub fn rustc_flags_capture(args: TokenStream) -> TokenStream { rustc::flags_capture(args) }
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn refutable(args: TokenStream, input: TokenStream) -> TokenStream { refutable::refutable(args, input) }
|
pub fn refutable(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
attribute_macro::<ItemFn, _>(args, input, refutable::refutable)
|
||||||
|
}
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn implement(args: TokenStream, input: TokenStream) -> TokenStream { implement::implement(args, input) }
|
pub fn implement(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
attribute_macro::<ItemFn, _>(args, input, implement::implement)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attribute_macro<I, F>(args: TokenStream, input: TokenStream, func: F) -> TokenStream
|
||||||
|
where
|
||||||
|
F: Fn(I, &[Meta]) -> Result<TokenStream>,
|
||||||
|
I: Parse,
|
||||||
|
{
|
||||||
|
let item = parse_macro_input!(input as I);
|
||||||
|
syn::punctuated::Punctuated::<Meta, syn::Token![,]>::parse_terminated
|
||||||
|
.parse(args)
|
||||||
|
.map(|args| args.iter().cloned().collect::<Vec<_>>())
|
||||||
|
.and_then(|ref args| func(item, args))
|
||||||
|
.unwrap_or_else(|e| e.to_compile_error().into())
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
use proc_macro::{Span, TokenStream};
|
use proc_macro::{Span, TokenStream};
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
use syn::{parse_macro_input, AttributeArgs, FnArg::Typed, Ident, ItemFn, Pat, PatIdent, PatType, Stmt};
|
use syn::{FnArg::Typed, Ident, ItemFn, Meta, Pat, PatIdent, PatType, Stmt};
|
||||||
|
|
||||||
pub(super) fn refutable(args: TokenStream, input: TokenStream) -> TokenStream {
|
use crate::Result;
|
||||||
let _args = parse_macro_input!(args as AttributeArgs);
|
|
||||||
let mut item = parse_macro_input!(input as ItemFn);
|
|
||||||
|
|
||||||
|
pub(super) fn refutable(mut item: ItemFn, _args: &[Meta]) -> Result<TokenStream> {
|
||||||
let inputs = item.sig.inputs.clone();
|
let inputs = item.sig.inputs.clone();
|
||||||
let stmt = &mut item.block.stmts;
|
let stmt = &mut item.block.stmts;
|
||||||
let sig = &mut item.sig;
|
let sig = &mut item.sig;
|
||||||
|
@ -25,16 +24,10 @@ pub(super) fn refutable(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
let variant = &pat.path;
|
let variant = &pat.path;
|
||||||
let fields = &pat.fields;
|
let fields = &pat.fields;
|
||||||
|
|
||||||
// new versions of syn can replace this kronecker kludge with get_mut()
|
let Some(Typed(PatType {
|
||||||
for (j, input) in sig.inputs.iter_mut().enumerate() {
|
|
||||||
if i != j {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let Typed(PatType {
|
|
||||||
ref mut pat,
|
ref mut pat,
|
||||||
..
|
..
|
||||||
}) = input
|
})) = sig.inputs.get_mut(i)
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
@ -53,9 +46,8 @@ pub(super) fn refutable(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
let #variant { #( #field ),*, .. } = #name else { panic!("incorrect variant passed to function argument {i}"); };
|
let #variant { #( #field ),*, .. } = #name else { panic!("incorrect variant passed to function argument {i}"); };
|
||||||
};
|
};
|
||||||
|
|
||||||
stmt.insert(0, syn::parse2::<Stmt>(refute).expect("syntax error"));
|
stmt.insert(0, syn::parse2::<Stmt>(refute)?);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
item.into_token_stream().into()
|
Ok(item.into_token_stream().into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,18 @@
|
||||||
|
use syn::{Expr, Lit, Meta};
|
||||||
|
|
||||||
|
pub(crate) fn get_named_string(args: &[Meta], name: &str) -> Option<String> {
|
||||||
|
args.iter().find_map(|arg| {
|
||||||
|
let value = arg.require_name_value().ok()?;
|
||||||
|
let Expr::Lit(ref lit) = value.value else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let Lit::Str(ref str) = lit.lit else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
value.path.is_ident(name).then_some(str.value())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn camel_to_snake_string(s: &str) -> String {
|
pub(crate) fn camel_to_snake_string(s: &str) -> String {
|
||||||
let mut output = String::with_capacity(
|
let mut output = String::with_capacity(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue