proc-macro-workshop:debug-2
审题
// Emit an implementation of std::fmt::Debug for a basic struct with named
// fields and no generic type parameters.
//
// Note that there is no enforced relationship between the name of a derive
// macro and the trait that it implements. Here the macro is named CustomDebug
// but the trait impls it generates are for Debug. As a convention, typically
// derive macros implement a trait with the same name as a macro.
//
//
// Resources:
//
// - The Debug trait:
// https://doc.rust-lang.org/std/fmt/trait.Debug.html
//
// - The DebugStruct helper for formatting structs correctly:
// https://doc.rust-lang.org/std/fmt/struct.DebugStruct.html
use derive_debug::CustomDebug;
#[derive(CustomDebug)]
pub struct Field {
name: &'static str,
bitmask: u8,
}
fn main() {
let f = Field {
name: "F",
bitmask: 0b00011100,
};
let debug = format!("{:?}", f);
assert!(debug.starts_with(r#"Field { name: "F","#));
}
参考链接
这里主要就是去实现Debug
,其中主要的类型当然还是fields
。
抽取
pub(crate) type FieldsType = syn::punctuated::Punctuated<syn::Field, syn::Token!(,)>;
pub(crate) fn parse_fields(ast: &syn::DeriveInput) -> syn::Result<&FieldsType> {
if let syn::Data::Struct(syn::DataStruct {
fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
..
}) = ast.data
{
return syn::Result::Ok(named);
}
syn::Result::Err(syn::Error::new_spanned(ast, "parse fields error"))
}
我只能说,和结构相关的话,fields
实在是太过频繁,纵使不太理解,死记硬背也行。
题解
pub(super) fn solution(
fields: &crate::common::FieldsType,
origin_ident: &syn::Ident,
) -> syn::Result<proc_macro2::TokenStream> {
let field_stream_vec: Vec<_> = fields
.iter()
.map(|f| {
let ident = &f.ident.as_ref();
let ident_string = ident.unwrap().to_string();
quote::quote! {
.field(#ident_string, &self.#ident)
}
})
.collect();
let origin_ident_string = origin_ident.to_string();
syn::Result::Ok(quote::quote! {
impl std::fmt::Debug for #origin_ident {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
fmt.debug_struct(#origin_ident_string)
#(
#field_stream_vec
)*
.finish()
}
}
})
}
这里不得不提的一点是,我们在quote::quote!
中,传入的数据要明确的区分类型。
这一点很重要,因为模板里面涉及到字面量和标识符。
这个问题在debug
这道题中时刻需要注意,因为ident
和string
使用方式不一样。
当然,如果你想”{#ident}”.to_string()或许也算是降低理解难度的一种方法。
整体
// lib.rs
mod common;
mod solution2;
fn solution1(ast: &syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
let origin_ident = &ast.ident;
let fields = crate::common::parse_fields(&ast)?;
// soluton2
let token_stream = solution2::solution(fields, origin_ident)?;
syn::Result::Ok(token_stream)
}
本作品采用《CC 协议》,转载必须注明作者和本文链接