struct - rust - 如何使用派生宏来解析结构的元信息?
问题描述
我正在使用struct来构建数据库的架构,所以我需要struct的元信息。
假设我的模式结构定义如下:
#[derive(ParseStruct)]
pub struct User {
#[field_attr( unique = true, default = "", index=["@hash", "@count"] )]
pub name: String,
#[field_attr( unique = true, default = "" )]
pub username: String,
pub description: Option<String>,
pub age: u32
}
我想解析成以下结构:
pub struct Schema<T>{
name: String, // the struct name
origin: T, // the struct to be parse, like the User struct above.
fields: Vec<Field>, // the struct's fields
}
pub struct Field {
field_name: String,
field_type: String,
field_attrs: FieldAttribute
}
pub struct FieldAttribute {
unique: bool,
default: String,
index: Vec<String> // Index of the database
}
我写了一个开头,但我不知道如何继续写:
#[proc_macro_derive(ParseStruct)]
pub fn parse_struct_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
let name = &ast.ident;
let gen = quote! {
impl #name {
fn parsed_schema()-> Schema {
// return the parsed schema
//...
}
}
};
gen.into()
}
预期结果:
use parse_struct::ParseStruct;
#[derive(ParseStruct)]
struct User{
#[field_attr( unique = true, default = "", index=["@hash", "@count"] )]
pub name: String
}
fn main() {
let schema = User::parsed_schema();
println!("{:#?}",schema);
}
我不知道如何实现它。
我最近才开始学习派生宏,还没有完全掌握。在 Internet 上很难找到有关派生宏的有用教程。
请帮助我,谢谢。
解决方案
使用 proc_macro_derive(过程宏)
src/main.rs:
use print_struct_trait::PrintStruct;
#[derive(PrintStruct)]
struct Point {
name: String,
x: i32,
y: i32,
}
fn main() {
let point = Point {
name: "origin".to_string(),
x: 2,
y: 3,
};
point.print();
}
输出:
key=name, value=origin, type=String
key=x, value=2, type=i32
key=y, value=3, type=i32
print_struct_derive/src/lib.rs:
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::spanned::Spanned;
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(PrintStruct, attributes(serde))]
pub fn derive_signature(item: TokenStream) -> TokenStream {
let ast = parse_macro_input!(item as DeriveInput);
let struct_name = &ast.ident;
let fields = if let syn::Data::Struct(syn::DataStruct {
fields: syn::Fields::Named(ref fields),
..
}) = ast.data
{
fields
} else {
panic!("Only support Struct")
};
let mut keys = Vec::new();
let mut idents = Vec::new();
let mut types = Vec::new();
for field in fields.named.iter() {
let field_name: &syn::Ident = field.ident.as_ref().unwrap();
let name: String = field_name.to_string();
let literal_key_str = syn::LitStr::new(&name, field.span());
let type_name = &field.ty;
keys.push(quote! { #literal_key_str });
idents.push(&field.ident);
types.push(type_name.to_token_stream());
}
let expanded = quote! {
impl PrintStruct for #struct_name {
fn print(&self) {
#(
println!(
"key={key}, value={value}, type={type_name}",
key = #keys,
value = self.#idents,
type_name = stringify!(#types)
);
)*
}
}
};
expanded.into()
}
github上PrintStruct的源代码
参考:
推荐阅读
- android - 我在加载插页式广告时不断收到此错误
- javascript - 如何声明对现有命名空间的引用,该命名空间可在运行时从 JavaScript 包中获得
- cordova - tel: 和 mailto: 链接仅在 iOS13 上不起作用
- javascript - 使用固定菜单调整锚点到其他页面
- python-3.x - 调试 any() 行时的 GeneratorExit 异常
- javascript - 将 NPM 项目转换为与浏览器兼容的 JavaScript 文件?
- c - 将所有数组元素复制到C中的另一个数组中
- python - 如何在 Python 中将对象数组保存到文件中
- django - Django:只想循环 _set 获取 pk 值
- php - Symfony 项目使用 Nginx 的 NetworkError