proc-macro-workshop:builder-3-5

godme發表於2022-06-29
// Generate methods on the builder for setting a value of each of the struct
// fields.
//
//     impl CommandBuilder {
//         fn executable(&mut self, executable: String) -> &mut Self {
//             self.executable = Some(executable);
//             self
//         }
//
//         ...
//     }

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());
}

這道題清晰明瞭,主要是為XXBuilder實現各種的setter方法。
相關的模板和fields之前都已經獲取好了,這裡直接實現就行了。

值的注意的是,實現setter方法的時候經常會進行鏈式呼叫。
雖然用例裡面都是分開呼叫的,但是在第五關的用例中,會進行鏈式呼叫

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

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

因此,在實現的時候順便返回自身物件的引用,合二為一更方便。

// solution35.rs
pub(super) fn soultion(
    fields: &crate::common::FieldsType,
    builder_ident: &syn::Ident,
) -> proc_macro2::TokenStream {
    let idents: Vec<_> = fields.iter().map(|f| &f.ident).collect();
    let tys: Vec<_> = fields.iter().map(|f| &f.ty).collect();

    quote::quote! {
        impl #builder_ident {
            #(
                pub fn #idents(&mut self, #idents: #tys) -> &mut Self {
                    self.#idents = std::option::Option::Some(#idents);
                    // 返回自身引用,順便解決第五題鏈式呼叫
                    self
                }
            )*
        }
    }
}

可以看到,除了基本的模板,我們主要依靠解析的資訊讓單一的模板多樣化。

// lib.rs
mod solution2;
mod solution35;

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 solution35_stream = solution35::soultion(fields, builder_ident);
    token_stream.extend(solution35_stream);

    proc_macro::TokenStream::from(token_stream)
本作品採用《CC 協議》,轉載必須註明作者和本文連結