场景:
如下 Command 结构体
#[derive(Builder)]
pub struct Command {
executable: String,
args: Vec<String>,
env: Vec<String>,
current_dir: Option<String>,
}
解析语法树判断 current_dir 字段是否为 Option 类型, 如果是 Option 获取内部的 String 类型。
打印语法树
#[proc_macro_derive(Builder)]
pub fn derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
println!("{:#?}", input);
TokenStream::new()
}
字段 current_dir 对应语法树输出结果如下:
Field {
attrs: [],
vis: Visibility::Inherited,
mutability: FieldMutability::None,
ident: Some(
Ident {
ident: "current_dir",
span: #0 bytes(289..300),
},
),
colon_token: Some(
Colon,
),
ty: Type::Path {
qself: None,
path: Path {
leading_colon: None,
segments: [
PathSegment {
ident: Ident {
ident: "Option",
span: #0 bytes(302..308),
},
arguments: PathArguments::AngleBracketed {
colon2_token: None,
lt_token: Lt,
args: [
GenericArgument::Type(
Type::Path {
qself: None,
path: Path {
leading_colon: None,
segments: [
PathSegment {
ident: Ident {
ident: "String",
span: #0 bytes(309..315),
},
arguments: PathArguments::None,
},
],
},
},
),
],
gt_token: Gt,
},
},
],
},
},
},
解析当前字段的 Type 类型数据
// 要返回的类型数据
struct InnerTypeData {
// 是否为 Option 类型
is_option: bool,
// 具体内部类型
ty: Type,
}
/// 获取字段类型的内部类型
/// ty: 当前字段的Type数据
fn get_inner_type(ty: &Type) -> Result<InnerTypeData, Box<dyn std::error::Error>> {
// 默认值
let mut inner_ty_data = InnerTypeData {
is_option: false,
ty: ty.clone(),
};
if let syn::Type::Path(ref type_path) = ty {
// 外层 segments 必须有值
if type_path.path.segments.len() <= 0 {
return Err("Segments empty".into());
}
// 外层 segments 第一条数据
let path_segments = type_path.path.segments.first().unwrap();
// 外层类型是 Option
if path_segments.ident.eq("Option") {
inner_ty_data.is_option = true;
}
// 不是 option 类型直接返回
if !inner_ty_data.is_option {
return Ok(inner_ty_data);
}
// 是 Option 再解构出 Option 内层类型
if let syn::PathArguments::AngleBracketed(ref path_args) = path_segments.arguments {
if path_args.args.len() <= 0 {
return Err("Args 数据为空".into());
}
// 获取到内部类型
if let syn::GenericArgument::Type(ref arg_type) = path_args.args.first().unwrap() {
inner_ty_data.ty = arg_type.clone();
}
}
}
Ok(inner_ty_data)
}
示例:根据 InnerTypeData 生成 setters 方法
// setter 设置值参数
// 遍历字段列表
let setter_fns = fields.iter().map(|f| {
// 当前字段名称
let name = &f.ident;
// 当前字段类型
let ty = &f.ty;
// 内部类型数据
let inner_type_data = get_inner_type(ty).unwrap();
// 具体的内部类型
let inner_type = inner_type_data.ty;
quote! {
pub fn #name(&mut self, #name: #inner_type) -> &mut Self {
self.#name = Some(#name);
self
}
}
});
quote! {
impl #builder_name {
// 建造者 setters 插值遍历
#(#setter_fns)*
}
}
cargo expand 输出结果:
impl CommandBuilder {
pub fn executable(&mut self, executable: String) -> &mut Self {
self.executable = Some(executable);
self
}
pub fn args(&mut self, args: Vec<String>) -> &mut Self {
self.args = Some(args);
self
}
pub fn env(&mut self, env: Vec<String>) -> &mut Self {
self.env = Some(env);
self
}
pub fn current_dir(&mut self, current_dir: String) -> &mut Self {
self.current_dir = Some(current_dir);
self
}
}