-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
rust宏 #11
Comments
struct 派生宏 个人实现版本 参考use proc_macro::TokenStream;
use syn::{spanned::Spanned};
// use syn::{parse_quote};
use quote;
pub fn derive(input: TokenStream) -> TokenStream {
let st = syn::parse_macro_input!(input as syn::DeriveInput);
// TokenStream::new()
match do_expand(&st) {
Ok(token_stream) => token_stream.into(),
Err(e) => e.to_compile_error().into(),
}
}
type StructFields = syn::punctuated::Punctuated<syn::Field, syn::Token![,]>;
fn get_fields_from_derive_input(st: &syn::DeriveInput) -> syn::Result<&StructFields> {
if let syn::Data::Struct(
syn::DataStruct {
fields: syn::Fields::Named(
syn::FieldsNamed {
ref named,
..
}
),
..
}
) = st.data {
Ok(named)
} else {
Err(syn::Error::new_spanned(st, "Must Define On Struct, Not Allow On Enum Or Union".to_string()))
}
}
fn get_generic_inner_type<'a>(t: &'a syn::Type, out_ident_name: &str) -> Option<&'a syn::Type> {
if let syn::Type::Path(
syn::TypePath {
path: syn::Path {
segments,
..
},
..
}
) = t {
if let Some(seg) = segments.last() {
if seg.ident.to_string() == out_ident_name {
if let syn::PathArguments::AngleBracketed(
syn::AngleBracketedGenericArguments {
args,
..
}
) = &seg.arguments {
if let Some(syn::GenericArgument::Type(inner_type)) = args.first() {
return Some(inner_type);
}
}
}
}
}
None
}
enum AttrType {
Struct,
// Field,
}
fn get_attr(attrs: &Vec<syn::Attribute>, attr_name: &str, attr_type: AttrType) -> syn::Result<Option<syn::Ident>> {
// #[arel="users"]
// for attr in attrs {
// if let Ok(
// syn::Meta::NameValue(
// syn::MetaNameValue {
// ref path,
// ref lit,
// ..
// }
// )
// ) = attr.parse_meta() {
// if path.is_ident("arel") {
// if let syn::Lit::Str(ref ident_str) = lit {
// return Ok(Some(syn::Ident::new(ident_str.value().as_str(), attr.span())));
// }
// }
// }
// }
// #[arel(table_name="users")]
for attr in attrs {
if let Ok(
syn::Meta::List(
syn::MetaList {
ref path,
ref nested,
..
}
)
) = attr.parse_meta() {
for segment in path.segments.iter() {
if segment.ident == "arel" {
for nested_item in nested.iter() {
if let syn::NestedMeta::Meta(syn::Meta::NameValue(kv)) = nested_item {
if let syn::Lit::Str(ref ident_str) = kv.lit {
if kv.path.is_ident(attr_name) {
return Ok(Some(syn::Ident::new(ident_str.value().as_str(), attr.span())));
} else { // 校验
match attr_type {
AttrType::Struct => {
let support_attr_names = vec!["table_name"];
for attr_name in &support_attr_names {
if !kv.path.is_ident(attr_name) {
return Err(syn::Error::new_spanned(&kv.path, format!("Struct Only Support Attrs: {:?}", &support_attr_names)));
}
}
},
// AttrType::Field => {
// let support_attr_names = vec!["table_column_name"];
// for attr_name in &support_attr_names {
// if !kv.path.is_ident(attr_name) {
// return Err(syn::Error::new_spanned(&kv.path, format!("Field Only Support Attrs: {:?}", &support_attr_names)));
// }
// }
// }
}
}
}
}
}
}
}
}
}
Ok(None)
}
fn builde_struct_fields(st: &syn::DeriveInput) -> syn::Result<(proc_macro2::TokenStream, proc_macro2::TokenStream, proc_macro2::TokenStream, proc_macro2::TokenStream)> {
let fields = get_fields_from_derive_input(st)?;
let idents: Vec<_> = fields.iter().map(|f| &f.ident).collect();
let types: Vec<_> = fields.iter().map(|f| &f.ty).collect();
Ok(
(
// first
{
let types: Vec<_> = fields.iter().map(|f| {
// &f.ty
if let Some(ty_inner_type) = get_generic_inner_type(&f.ty, "Option") {
quote::quote! {
std::option::Option<#ty_inner_type>
}
} else if let Some(_) = get_generic_inner_type(&f.ty, "Vec") {
let origin_type = &f.ty;
quote::quote! {
#origin_type
}
} else {
let origin_type = &f.ty;
quote::quote! {
std::option::Option<#origin_type>
}
}
}).collect();
quote::quote! {
#(#idents: #types),*
}
},
// second
// quote::quote! {
// #(#idents: std::option::Option::None),*
// },
{
let mut final_token_stream = proc_macro2::TokenStream::new();
for (ident, r#type) in idents.iter().zip(types.iter()) {
let token_stream_piece;
if let Some(_) = get_generic_inner_type(r#type, "Vec") {
token_stream_piece = quote::quote! {
#ident: std::vec::Vec::new(),
};
} else {
token_stream_piece = quote::quote! {
#ident: std::option::Option::None,
};
}
final_token_stream.extend(token_stream_piece);
}
final_token_stream
},
// third
// quote::quote! {
// #(pub fn #idents(&mut self, #idents: #types) -> &mut Self {
// self.#idents = std::option::Option::Some(#idents);
// self
// })*
// },
{
let mut final_token_stream = proc_macro2::TokenStream::new();
for (ident, r#type) in idents.iter().zip(types.iter()) {
let token_stream_piece;
if let Some(_) = get_generic_inner_type(r#type, "Vec") {
token_stream_piece = quote::quote! {
fn #ident(&mut self, #ident: #r#type) -> &mut Self {
self.#ident = #ident;
self
}
};
} else {
// T -> T, Option<T> -> T
let r#type = if let Some(inner_type) = get_generic_inner_type(r#type, "Option") { inner_type } else { r#type };
token_stream_piece = quote::quote! {
fn #ident(&mut self, #ident: #r#type) -> &mut Self {
self.#ident = std::option::Option::Some(#ident);
self
}
};
}
final_token_stream.extend(token_stream_piece);
}
final_token_stream
},
// fourth
{
let segments: Vec<_> = fields.iter().filter(|f| {
get_generic_inner_type(&f.ty, "Option").is_none() && get_generic_inner_type(&f.ty, "Vec").is_none()
}).map(|f| {
let ident = &f.ident;
quote::quote! {
if self.#ident.is_none() {
return Err(anyhow::anyhow!("{} Not Allow None", stringify!(#ident)));
}
}
}).collect();
quote::quote! {
pub fn validate(&self) -> anyhow::Result<()> {
#(#segments)*
Ok(())
}
}
}
)
)
}
fn do_expand(st: &syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
let struct_ident = &st.ident;
let struct_name_literal = struct_ident.to_string();
let arel_struct_name_literal = format!("{}Arel", struct_name_literal);
let arel_struct_ident = &syn::Ident::new(&arel_struct_name_literal, st.span());
let (
builder_fields_def,
builder_fields_init_clauses,
builder_setter_functions,
validate_function,
) = builde_struct_fields(st)?;
// table_name_function
let table_name_function = {
if let Some(table_name_ident) = get_attr(&st.attrs, "table_name", AttrType::Struct)? {
quote::quote! {
fn table_name() -> String {
stringify!(#table_name_ident).to_string()
}
}
} else {
proc_macro2::TokenStream::new()
}
};
// trait限定 使用eg: impl #impl_generics #arel_struct_ident #type_generics #where_clause
// let mut generics = st.generics.clone();
// let (impl_generics, type_generics, where_clause) = {
// // for param in generics.params.iter_mut() {
// // if let syn::GenericParam::Type(t) = param {
// // // 对每个trait 额外增加Debug限定 eg: struct User<T: Clone> => struct User<T: Clone + Debug>
// // t.bounds.push(parse_quote!(std::fmt::Debug))
// // }
// // }
// generics.split_for_impl()
// };
let ret = quote::quote! {
// User
impl #struct_ident {
fn table() -> arel::table::Table<#arel_struct_ident> { #arel_struct_ident::table() }
fn query() -> arel::table::Table<#arel_struct_ident> { #arel_struct_ident::query() }
fn lock() -> arel::table::Table<#arel_struct_ident> { #arel_struct_ident::lock() }
fn create(condition: serde_json::Value) -> arel::table::Table<#arel_struct_ident> { #arel_struct_ident::create(condition) }
fn update_all(condition: serde_json::Value) -> arel::table::Table<#arel_struct_ident> { #arel_struct_ident::update_all(condition) }
fn delete_all(condition: serde_json::Value) -> arel::table::Table<#arel_struct_ident> { #arel_struct_ident::delete_all(condition) }
}
// UserArel
#[derive(::core::clone::Clone, ::core::fmt::Debug)]
pub struct #arel_struct_ident {
#builder_fields_def
}
impl arel::ArelAble for #arel_struct_ident {
#table_name_function
fn primary_key() -> &'static str { "id" }
}
impl #arel_struct_ident {
pub fn new() -> Self {
Self {
#builder_fields_init_clauses
}
}
#builder_setter_functions
#validate_function
}
};
Ok(ret)
} |
其他rust宏解析器 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
属性式过程宏
参考资料: https://blog.ideawand.com/2021/02/27/rust_procedural_macro/rust_proc_marco_workshop_guide-01/
初始化项目
cargo new my_demo --bin cd my_demo cargo new proc_macro_first --lib
my_demo/proc_macro_first/Cargo.toml
, 添加:my_demo/proc_macro_first/src/lib.rs
, 添加:my_demo/Cargo.toml
, 添加:my_demo/src/main.rs
, 添加:执行`cargo check`
* 使用
syn
将TokenStream
转换成更高级的TokenStream
,quoto!
可以将高级的TokenStream
转换回函数支持的返回值类型的TokenStream
进行返回my_demo/proc_macro_first/Cargo.toml
, 添加:my_demo/proc_macro_first/src/lib.rs
, 添加:执行`cargo check`
派生式过程宏
derive
参考资料
* 第一关
my_demo/proc_macro_first/src/lib.rs
, 写入:my_demo/src/main.rs
, 写入:执行`cargo check`
* 其他关卡,完整代码
my_demo/proc_macro_first/Cargo.toml
, 添加:my_demo/proc_macro_first/src/lib.rs
, 更改为:# cargo install cargo-expand cargo expand
生成结果打印如下:
The text was updated successfully, but these errors were encountered: