proc-macro-workshop:builder-4

审题

// Generate a `build` method to go from builder to original struct.
//
// This method should require that every one of the fields has been explicitly
// set; it should return an error if a field is missing. The precise error type
// is not important. Consider using Box<dyn Error>, which you can construct
// using the impl From<String> for Box<dyn Error>.
//
//     impl CommandBuilder {
//         pub fn build(&mut self) -> Result<Command, Box<dyn Error>> {
//             ...
//         }
//     }

use derive_builder::Builder;

#[derive(Builder)]
pub struct Command {
    executable: String,
    args: Vec<String>,
    env: Vec<String>,
    current_dir: String,
}

fn main() {
    let mut builder = Command::builder();
    builder.executable("cargo".to_owned());
    builder.args(vec!["build".to_owned(), "--release".to_owned()]);
    builder.env(vec![]);
    builder.current_dir("..".to_owned());

    let command = builder.build().unwrap();
    assert_eq!(command.executable, "cargo");
}

如提示所言,我们这一关主要的是实现build方法,同时注意返回结果类型为Result
原来的数据我们设置在了XBuilder中,这一关就需要从XBuilder中反向注入参数了。

这里需要扩展的是,一般操作过程错误处理需要返回Result
并且对于一个Command而言,可以有一些缺省参数。
在这里,我们将检查全部字段,如果没有设置,返回异常。
虽然这里没有对异常进行检查,但是后续的问题中,对异常的检查会十分严格。

题解

pub(super) fn solution(
    fields: &crate::common::FieldsType,
    builder_ident: &syn::Ident,
    origin_ident: &syn::Ident,
) -> proc_macro2::TokenStream {
    let idents: Vec<_> = fields.iter().map(|f| &f.ident).collect();
    quote::quote! {
        impl #builder_ident {
            pub fn build(&self) -> std::result::Result<#origin_ident, std::boxed::Box<dyn std::error::Error>> {
                // 异常检查展开
                #(
                    if self.#idents.is_none() {
                        let err = std::format!("field {} missing", std::stringify!(#idents));
                        return std::result::Result::Err(err.into());
                    }
                )*
                // 数值回传
                let res = #origin_ident {
                    #(
                        #idents: self.#idents.clone().unwrap()
                    ),*
                };
                std::result::Result::Ok(res)
            }
        }
    }
}

整体


fn solution1(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let ast = syn::parse_macro_input!(input as syn::DeriveInput);
    let fields = {
        match common::parse_fields(&ast) {
            Ok(f) => f,
            Err(_e) => std::panic!(std::stringify!(_e)),
        }
    };

    let origin_ident = &ast.ident;
    let builder_ident = &quote::format_ident!("{}Builder", origin_ident);
    let mut token_stream = proc_macro2::TokenStream::new();

    // solution2
    let solution2_stream = solution2::solution(fields, origin_ident, builder_ident);
    token_stream.extend(solution2_stream);

    // solution35
    let solution3_stream = solution35::soultion(fields, builder_ident);
    token_stream.extend(solution3_stream);

    // solution4
    let solution45_stream = solution4::solution(fields, builder_ident, origin_ident);
    token_stream.extend(solution45_stream);

    proc_macro::TokenStream::from(token_stream)
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!