ã¯ããã« builder ãã¯ããäœãïŒç¶ãïŒ 06-optional-field ç®æš å®è£
æ¹é å®è£
ã¬ãŒãç¯ã§ Optional ã§ãªãåã®ã¿ãšã©ãŒãåºãããã«ãã Optionã§ã©ãããããåã¯ã¢ã³ã©ããããŠCommandBuilderæ§é äœã®ãã£ãŒã«ãã§ä¿æãã Optional ãªåã® setter ã¡ãœããã¯ã©ãããããäžèº«ã®åãåŒæ°ãšããŠåãä»ããããã«ãã is_optionãšunwrap_optionã®å®è£
is_option颿° unwrap_option颿° AngleBracketed(AngleBracketedGenericArguments) ãªãã¡ã¯ã¿ãªã³ã° 07-repeated-field ç®æš å®è£
æ¹é å®è£
Builder æ§é äœã®ãã£ãŒã«ãã§ã¯Vecåã®å€æ°ã¯Optionã§ã©ããããªã is_vectorãšunwrap_vectorã®å®è£
ãã£ãŒã«ãã«ä»äžãããã¢ããªãã¥ãŒããååŸãã List Meta(Meta) Lit(Lit) Vecåã®ãã£ãŒã«ãã® setter ãäžæ¬æŽæ°çšãšèŠçŽ è¿œå çšã® 2 çš®é¡çæãã 08-unrecognized-attribute ç®æš å®è£
æ¹é å®è£
09-redefined-prelude-types ç®æš å®è£
æ¹é å®è£
ãŸãšã åèæç® ã¯ããã« åç·š ã§ã¯ã setter ã¡ãœããã«ããå€ã®èšå®ã build ã¡ãœããã«ããæ§é äœã®çæãªã©ã®åºæ¬çãªæ©èœãæã£ãæç¶ãçãã¯ããå®è£
ããŸãããåŸç·šã§ã¯ä»¥äžã®æ©èœãå®è£
ããŠãããŸãã Optional ãªå€ãæ§é äœã®ãã£ãŒã«ããšããŠæãŠãããã«ãã 以äžã® 2 ã€ã®æ¹æ³ã§ Vec åã®ãã£ãŒã«ããæŽæ°ã§ããããã«ãã ãã¯ã¿ãäžããŠäžæ¬ã§æŽæ°ãã ãã¯ã¿ã®èŠçŽ ãäžã㊠1 ã€ãã€ãã£ãŒã«ãã«èŠçŽ ã远å ãã ã³ã³ãã€ã«ãšã©ãŒãçºçããéã«ããããããã¡ãã»ãŒãžã衚瀺ãã builder ãã¯ããäœãïŒç¶ãïŒ 06-optional-field ç®æš æ§é äœã®ãã£ãŒã«ããšã㊠Optional ãªå€ãæãŠãããã«ãã Optional ãªãã£ãŒã«ãã«ã¯å€ãå
¥ã£ãŠããªããŠã build ã¡ãœããã§æ§é äœãçæã§ãã Optional ã§ãªããã£ãŒã«ãã¯å€ãå
¥ã£ãŠããªããš build ã¡ãœããã§æ§é äœãçæã§ããªã Optional ãªãã£ãŒã«ã㯠Some ã§ã©ããããã«äžèº«ã®å€ããã®ãŸãŸäœ¿ã£ãŠåæåã§ãã æåŸã®é
ç®ã«ã€ããŠã§ãããããšãã° Command æ§é äœã以äžã®ããã«ãªã£ãŠããå Žåã pub struct Command { executable: String , args: Vec < String > , env: Vec < String > , current_dir: Option < String > , } current_dir ã¯ä»¥äžã®ããã« String ãæž¡ãã ãã§ãããšããããšã§ãã let command = Command :: builder () . executable ( "cargo" . to_owned ()) . args ( vec! [ "build" . to_owned (), "--release" . to_owned ()]) . env ( vec! []) . current_dir ( ".." . to_owned ()) . build () . unwrap (); å®è£
æ¹é ç®æšãšããæ©èœãå®çŸããããã«å®è£
ããå¿
èŠãããã®ã¯ä»¥äžã®é
ç®ã§ãããŸãã¯ãããã®æ©èœãå®è£
ããŠãããŸãããã ã¬ãŒãç¯ã§ Optional ã§ãªãåã®ã¿ãšã©ãŒãåºãããã«ãã Option ã§ã©ãããããåã¯ã¢ã³ã©ããã㊠CommandBuilder æ§é äœã®ãã£ãŒã«ãã§ä¿æãã ä»ã¯å
ã®åãäœã§ãã£ãŠã Option ã§ã©ããããããã«ãªã£ãŠããŸãããã®ãããOptional ãªå㯠Option<Option<_>> ã®ããã«ãªããŸãããã®ãŸãŸã ãšæ±ãã¥ããã®ã§ãOptional ãªåã¯ãã£ããã¢ã³ã©ããããŠããã¹ãŠã®ãã£ãŒã«ãã®åã Option<_> ã«ãªãããã«ããŸã Optional ãªåã® setter ã¡ãœããã¯ã©ãããããäžèº«ã®åãåŒæ°ãšããŠåãä»ããããã«ãã å®è£
å®è£
æ¹éã§èª¬æããæ©èœãå®è£
ããŠãããŸãã ã¬ãŒãç¯ã§ Optional ã§ãªãåã®ã¿ãšã©ãŒãåºãããã«ãã ã¬ãŒãç¯ãçæããéšåã®å®è£
ã¯ä»¥äžã®ããã«ãªã£ãŠããŸããä»ã¯ãã¹ãŠã®ãã£ãŒã«ãã«å¯Ÿã㊠None ã§ãããã®ãã§ãã¯ãçæããŠããŸããããã Optional ã§ãªãåã®ã¿ã¬ãŒãç¯ãçæããããã«å€æŽããŸãã let checks = idents. iter (). map ( | ident | { let err = format! ( "Required field '{}' is missing" , ident. to_string ()); quote! { if self . #ident . is_none () { return Err ( #err . into ()) } } }); Optional ã§ãªããã£ãŒã«ãã®ã¿ã¬ãŒãç¯ãçæããããã«ã¯ãåãã£ãŒã«ãã®åãèŠãŠ Optional ã§ãªããã£ãŒã«ãã®ã¿ããã£ã«ã¿ããã°è¯ãããã§ãã types ã«åãã£ãŒã«ãã®åãæ ŒçŽãããŠããã®ã§ã以äžã®ããã« filter ãçšã㊠Optional ã§ãªããã£ãŒã«ãã®èå¥åã«ã€ããŠã ãã¬ãŒãç¯ãçæããŸãã is_option ã¯äžããããåã Optional ã§ãããã©ãããå€å®ããäœããã®é¢æ°ã§ããã®ã¡ã»ã©å®è£
ããŸãã let checks = idents . iter () . zip ( & types) . filter ( | (_, ty) | ! is_option (ty)) . map ( | (ident, _) | { let err = format! ( "Required field '{}' is missing" , ident. to_string ()); quote! { if self . #ident . is_none () { return Err ( #err . into ()) } } }); Option ã§ã©ãããããåã¯ã¢ã³ã©ããã㊠CommandBuilder æ§é äœã®ãã£ãŒã«ãã§ä¿æãã ä»ã® Builder æ§é äœã®å®çŸ©ã¯ä»¥äžã®ããã«ãªã£ãŠããŸãããã¹ãŠã®ãã£ãŒã«ãã Option ã§ã©ããããŠããŸããOptional ãªãã£ãŒã«ãã«ã€ããŠã¯ãããããã Option ã§ã©ãããããåãåãåºããŠããã°ãããšã®åŠçã¯ä»ãŸã§ãšåãå
容ã«ãªããŸãã #vis struct #builder_name { #( #idents : Option < #types > ), * } å®éã«å®è£
ããŠãããŸãã Option ã®äžèº«ã®åãåãåºããªã©ã®åŠçã远å ãããã®ã§ãçæããã Builder æ§é äœã®ãã£ãŒã«ãã builder_fields ã«ãã£ããä¿æããŠããšã§å±éããŸãããã builder_fields ã®å®è£
ã¯ä»¥äžã®ããã«ãªããŸãã unwrap_option 颿°ã§ Option ã«ã©ãããããŠããåãåãåºããŠãã以å€ã¯ä»ãŸã§ãšåãã§ãã unwrap_option 㯠is_option ãšåããã®ã¡ã»ã©å®è£
ããŸãã let builder_fields = idents. iter (). zip ( & types). map ( | (ident, ty) | { let t = unwrap_option (ty). unwrap_or (ty); quote! { #ident : Option < #t > } }); builder_fields 㯠Builder æ§é äœã®å®çŸ©ãçæããéšåã§å±éããŸãã #vis struct #builder_name { #( #builder_fields ), * } Builder æ§é äœã®å®çŸ©ãå€ãã£ããã build 颿°ãå€ããå¿
èŠããããŸããä»ã¯ä»¥äžã®ããã«ç®çã®æ§é äœãçæããéã«ãã¹ãŠã®ãã£ãŒã«ãã unwrap ããŠããŸãããOptional ãªãã£ãŒã«ã㯠unwrap ããå¿
èŠããªãã®ã§ãã®ãŸãŸè¿ãããã«ããŸãããã pub fn build ( &mut self ) -> Result < #ident , Box < dyn std :: error :: Error >> { #( #checks ) * Ok ( #ident { #( #idents : self . #idents . clone (). unwrap ()), * }) } Optional ãªãã£ãŒã«ããã©ãããå€å®ããåŠçã远å ãããããã builder_fields ãšåæ§ã«å¥ã®å€æ°ã«æ ŒçŽããŠã®ã¡ã»ã©å±éããŸããå
·äœçãªå®è£
ã¯ä»¥äžã®éãã§ãã is_option ã§ Optional ãªãã£ãŒã«ããã©ãããå€å®ããŠãOptional ã§ããã°å€ããã®ãŸãŸäœ¿çšããOptional ã§ãªããã° unwrap ããŠåŸãããå€ã䜿çšããŸãã let struct_fields = idents. iter (). zip ( & types). map ( | (ident, ty) | { if is_option (ty) { quote! { #ident : self . #ident . clone () } } else { quote! { #ident : self . #ident . clone (). unwrap () } } }); struct_fields 㯠build 颿°ã®äžã§å±éããŸãã pub fn build ( &mut self ) -> Result < #ident , Box < dyn std :: error :: Error >> { #( #checks ) * Ok ( #ident { #( #struct_fields ), * }) } Optional ãªåã® setter ã¡ãœããã¯ã©ãããããäžèº«ã®åãåŒæ°ãšããŠåãä»ããããã«ãã ä»ã® setter ã®å®è£
ã¯ä»¥äžã®ããã«ãªã£ãŠããŸããOptional ãªãã£ãŒã«ããã©ããã¯é¢ä¿ãªãããã¹ãŠã®ãã£ãŒã«ãã«ã€ããŠãã®ãã£ãŒã«ãã®åããã®ãŸãŸåãä»ããããã«ãªã£ãŠããŸãã impl #builder_name { #( pub fn #idents ( &mut self , #idents : #types ) -> &mut Self { self . #idents = Some ( #idents ); self }) * ... ã€ãŸã Optional ãªãã£ãŒã«ãã«ã€ããŠã¯ä»¥äžã®ãã㪠setter ãçæãããŸããããã§ã¯ Builder æ§é äœã®ãã£ãŒã«ãå®çŸ©ãšççŸããŸãããã®ãããOptional ãªãã£ãŒã«ãã® setter 颿°ã¯åŒæ°ãšã㊠Option ã®äžèº«ã®åãåãåãããã«ããŸãã pub fn current_dir ( &mut self , current_dir: Option < String > ) -> &mut Self { self .current_dir = Some (current_dir); self } å
·äœçã«ã¯ä»¥äžã®ãã㪠setter ãäœã£ãŠå±éããããã«æžãæããŸããBuilder æ§é äœã®ãã£ãŒã«ããšåæ§ã unwrap_option 颿°ã䜿ã£ãŠ Option ã§ã©ãããããäžèº«ã®åãåãåºããŸãã let setters = idents. iter (). zip ( & types). map ( | (ident, ty) | { let t = unwrap_option (ty). unwrap_or (ty); quote! { pub fn #ident ( &mut self , #ident : #t ) -> &mut Self { self . #ident = Some ( #ident ); self } } }); ... impl #builder_name { #( #setters ) * ... is_option ãš unwrap_option ã®å®è£
is_option ãš unwrap_option ãå®è£
ããŠãããŸãããŸã㯠is_option 颿°ããå®è£
ããŠãããŸãããã is_option 颿° is_option 㯠Type åãåãåã£ãŠããã Option ãã©ãããå€å®ãã颿°ãªã®ã§ã·ã°ããã£ã¯ä»¥äžã®ããã«ããã°è¯ãããã§ãã fn is_option (ty: & Type) -> bool ty ã Option ãã©ããå€å®ããã®ã«äœ¿ããã㪠Type ã®ã¡ãœãããããã確èªããŠã¿ãŸãããã syn ã¯ã¬ãŒãã®ããã¥ã¡ã³ã ã確èªãããšãã Type ã¯ä»¥äžã® enum ã®ããã§ãã pub enum Type { Array (TypeArray), BareFn (TypeBareFn), Group (TypeGroup), ImplTrait (TypeImplTrait), Infer (TypeInfer), Macro (TypeMacro), Never (TypeNever), Paren (TypeParen), Path (TypePath), Ptr (TypePtr), Reference (TypeReference), Slice (TypeSlice), TraitObject (TypeTraitObject), Tuple (TypeTuple), Verbatim (TokenStream), // some variants omitted } Option ãã©ã®ããªã¢ã³ãã«åé¡ããããã¯ãŸã ããããŸãããããšãããããã¿ãŒã³ãããã§åŠçããã°è¯ãããã§ãã fn is_option (ty: & Type) -> bool { match ty { todo! () } } ãã¿ãŒã³ãããã§åé¡ã§ãããã ãšãããšãããŸã§æ¹éãç«ãŠãããŸãããã Option ã¯ã©ã®ããªã¢ã³ãã«åé¡ãããã®ã§ãããããããã¥ã¡ã³ããäžèŠããŠãããããããã®ã¯èŠåœãããŸãããã Option 㯠Type::Path(syn::TypePath) ã«åé¡ãããŸãã TypePath 㯠std::iter::Iter ã®ãã㪠Path ãããŒã¹ããŠåŸãããæ§é äœã§ãã Type::Path ã«ãããããªãããªã¢ã³ãã«ã€ããŠã¯ãã®æç¹ã§ false ãè¿ããŠããŸã£ãŠåé¡ãªãã§ãããã fn is_option (ty: & Type) -> bool { match ty { Type :: Path (path) => todo! (), _ => false } } TypePath ã«ã¯ Option 以å€ã«ãäžè¿°ã® std::iter::Iter ã®ãããªãã®ãå«ãŸããŸããã©ã®ããã« Option ããã以å€ããå€å®ããã°è¯ãã§ããããã å
ã»ã©ã説æããããã«ã TypePath 㯠std::iter::Iter ã®ããã«ã³ãã³ 2 ã€ã§åå²ãããã»ã°ã¡ã³ãã®éåã§ãããã€ãŸããã»ã°ã¡ã³ãã®éåã®æåŸã®èŠçŽ ã Option ã§ãããã©ããã倿ããã°è¯ãããã§ãã ã§ã¯ãã©ã®ããã«ããŠã»ã°ã¡ã³ãã®éåã®æåŸã®èŠçŽ ãååŸããã°è¯ãã®ã§ããããã syn::TypePath ã¯ä»¥äžã®ãããªæ§é äœã§ã path ã« Path ã®æ
å ±ãä¿æããŠããŸãã syn::Path ã¯ä»¥äžã®ããã« segments ã«ã»ã°ã¡ã³ãã®éåãä¿æããŠããã segments.last() ã§æåŸã®èŠçŽ ã«ã¢ã¯ã»ã¹ã§ããŸãã pub struct TypePath { pub qself: Option < QSelf > , pub path: Path } pub struct Path { pub leading_colon: Option < Colon2 > , pub segments: Punctuated < PathSegment, Colon2 > , } äžèšãèžãŸãããšãçŸæç¹ã§ã®å®è£
ã¯ä»¥äžã®ããã«ãªããŸãã fn is_option (ty: & Type) -> bool { match ty { Type :: Path (path) => path.path.segments. last (), _ => false } } æåŸã«ãã»ã°ã¡ã³ãã®æåŸã®èŠçŽ ã®èå¥åã Option ãšäžèŽãããæ¯èŒããå¿
èŠããããŸãã segments.last() ã®æ»ãå€ã¯ PathSegment å ã§ããã ident ãã£ãŒã«ãããèå¥åã«ã¢ã¯ã»ã¹ã§ããŸãããã®èå¥åã Option ãã©ãããæ¯èŒããã°è¯ãããã§ãã æçµç㪠is_option 颿°ã®å®è£
ã¯ä»¥äžã®ããã«ãªããŸãã fn is_option (ty: & Type) -> bool { match ty { Type :: Path (path) => match path.path.segments. last (). unwrap () { Some (seg) => seg.ident == "Option" , None => false }, _ => false } } unwrap_option 颿° 次㯠unwrap_option 颿°ãå®è£
ããŠããŸãã unwrap_option 颿°ã¯äžããããåã Option ã§ããã° Some(å) ãè¿ããããã§ãªããã° None ãè¿ã颿°ãªã®ã§ãã·ã°ããã£ã¯ä»¥äžã®ããã«ããã°è¯ãã§ãããã fn unwrap_option (ty: & Type) -> Option < & Type > åŒæ°ã Option ã§ãªãå Žå㯠None ãè¿ããŸããåŒæ°ã Option ãã©ããã¯å
ã»ã©å®è£
ãã is_option 颿°ã䜿ããŸãã fn unwrap_option (ty: & Type) -> Option < & Type > { if ! is_option (ty) { return None ; } todo! () } 次㫠Option ã«ã©ãããããäžèº«ã®åãåãåºãåŠçãå®è£
ããŸããäžèº«ã®åã¯ã©ããã£ãŠåãåºãã°è¯ãã§ããããã is_option ã®å®è£
ã§ã§ãŠãã PathSegment ã®ãã£ãŒã«ãã«ã¯ ident 以å€ã«ãã 1 〠arguments ãšããã®ããããŸããããããé¢ä¿ãããããªã®ã§ã PathArguments ã®å®çŸ©ãèŠãŠèŠãŸãããã pub enum PathArguments { None , AngleBracketed (AngleBracketedGenericArguments), Parenthesized (ParenthesizedGenericArguments), } PathArguments 㯠enum ã®ããã§ããããªã¢ã³ãåãçºããŠã¿ããš AngleBracketed ãšãããã®ããããŸãããããé¢ä¿ããããã§ããå®éã ããã¥ã¡ã³ã ã® AngleBracketed ã®é
ç®ã«ã¯ä»¥äžã®ããã«èšèŒãããŠããããã®ããªã¢ã³ãããžã§ããªãã¯åŒæ°ã®æ
å ±ãæã£ãŠããããšãããããŸãã AngleBracketed(AngleBracketedGenericArguments) The <'a, T> in std::slice::iter<'a, T> . syn::AngleBracketedGenericArgument åã®å®çŸ©ãèŠãŠã¿ãŸãããã pub struct AngleBracketedGenericArguments { pub colon2_token: Option < Colon2 > , pub lt_token: Lt, pub args: Punctuated < GenericArgument, Comma > , pub gt_token: Gt, } ãã£ãŒã«ãåãçºããŠã¿ããšãžã§ããªãã¯åŒæ°ã®å㯠args ã«å
¥ã£ãŠãããã§ãã syn::Punctuated ãªã®ã§è€æ°ã®èŠçŽ ãããããã§ããã Option ã¯åŒæ°ã 1 ã€ããåããªãã®ã§ first() ã§ååŸããã°è¯ãã§ãããã fn unwrap_option (ty: & Type) -> Option < & Type > { if ! is_option (ty) { return None ; } match ty { Type :: Path (path) => path.path.segments. last (). map ( | seg | { match seg.arguments { PathArguments :: AngleBracketed ( ref args) => args.args. first (), _ => None } }), _ => None } } args.first() ã®æ»ãå€ã¯ Option<GenericArgument> ã§ãã GenericArgument ã¯ä»¥äžã®ãã㪠enum ã§ãåãå«ãŸããå Žå㯠GenericArgument::Type ããªã¢ã³ãã䜿çšãããã®ã§ã Type ããªã¢ã³ãã«ããããããã°è¯ãã§ãããã pub enum GenericArgument { Lifetime (Lifetime), Type (Type), Binding (Binding), Constraint (Constraint), Const (Expr), } äžèšãèžãŸãããšã unwrap_option ã®æçµçãªå®è£
ã¯ä»¥äžã®ããã«ãªããŸãã fn unwrap_option (ty: & Type) -> Option < & Type > { if ! is_option (ty) { return None ; } match ty { Type :: Path (path) => path.path.segments. last (). map ( | seg | { match seg.arguments { PathArguments :: AngleBracketed ( ref args) => { args.args. first (). and_then ( | arg | match arg { & GenericArgument :: Type ( ref ty) => Some (ty), _ => None , }) } _ => None } }), _ => None } } Path ã®æåŸã®èŠçŽ ãæã£ãŠããåŠçã¯ãŸãšããããã®ã§å¥ã®é¢æ°ãšããŠå€ã«åãåºããŸãããããçšã㊠is_option ãš unwrap_option ãæžãçŽããšãæçµçã«ã¯ä»¥äžã®ããã«ãªããŸãã fn get_last_path_segment (ty: & Type) -> Option < & PathSegment > { match ty { Type :: Path (path) => path.path.segments. last (), _ => None , } } fn is_option (ty: & Type) -> bool { match get_last_path_segment (ty) { Some (seg) => seg.ident == "Option" , _ => false , } } fn unwrap_option (ty: & Type) -> Option < & Type > { if ! is_option (ty) { return None ; } match get_last_path_segment (ty) { Some (seg) => match seg.arguments { PathArguments :: AngleBracketed ( ref args) => { args.args. first (). and_then ( | arg | match arg { & GenericArgument :: Type ( ref ty) => Some (ty), _ => None , }) } _ => None , }, None => None , } } æçµçãªå®è£
ã¯ä»¥äžã®ããã«ãªããŸãã use proc_macro :: TokenStream; use quote :: {format_ident, quote}; use syn :: { parse_macro_input, Data, DeriveInput, Fields, GenericArgument, Ident, PathArguments, PathSegment, Type, }; #[proc_macro_derive(Builder)] pub fn derive (input: TokenStream) -> TokenStream { let input = parse_macro_input! (input as DeriveInput); let ident = input.ident; let vis = input.vis; let builder_name = format_ident! ( "{}Builder" , ident); let (idents, types): ( Vec < Ident > , Vec < Type > ) = match input.data { Data :: Struct (data) => match data.fields { Fields :: Named (fields) => fields .named . into_iter () . map ( | field | { let ident = field.ident; let ty = field.ty; (ident. unwrap (), ty) }) . unzip (), _ => panic! ( "no unnamed fields are allowed" ), }, _ => panic! ( "expects struct" ), }; let builder_fields = idents. iter (). zip ( & types). map ( | (ident, ty) | { let t = unwrap_option (ty). unwrap_or (ty); quote! { #ident : Option < #t > } }); let checks = idents . iter () . zip ( & types) . filter ( | (_, ty) | ! is_option (ty)) . map ( | (ident, _) | { let err = format! ( "Required field '{}' is missing" , ident. to_string ()); quote! { if self . #ident . is_none () { return Err ( #err . into ()) } } }); let setters = idents. iter (). zip ( & types). map ( | (ident, ty) | { let t = unwrap_option (ty). unwrap_or (ty); quote! { pub fn #ident ( &mut self , #ident : #t ) -> &mut Self { self . #ident = Some ( #ident ); self } } }); let struct_fields = idents. iter (). zip ( & types). map ( | (ident, ty) | { if is_option (ty) { quote! { #ident : self . #ident . clone () } } else { quote! { #ident : self . #ident . clone (). unwrap () } } }); let expand = quote! { #vis struct #builder_name { #( #builder_fields ), * } impl #builder_name { #( #setters ) * pub fn build ( &mut self ) -> Result < #ident , Box < dyn std :: error :: Error >> { #( #checks ) * Ok ( #ident { #( #struct_fields ), * }) } } impl #ident { pub fn builder () -> #builder_name { #builder_name { #( #idents : None ), * } } } }; proc_macro :: TokenStream :: from (expand) } fn is_option (ty: & Type) -> bool { match get_last_path_segment (ty) { Some (seg) => seg.ident == "Option" , _ => false , } } fn unwrap_option (ty: & Type) -> Option < & Type > { if ! is_option (ty) { return None ; } match get_last_path_segment (ty) { Some (seg) => match seg.arguments { PathArguments :: AngleBracketed ( ref args) => { args.args. first (). and_then ( | arg | match arg { & GenericArgument :: Type ( ref ty) => Some (ty), _ => None , }) } _ => None , }, None => None , } } fn get_last_path_segment (ty: & Type) -> Option < & PathSegment > { match ty { Type :: Path (path) => path.path.segments. last (), _ => None , } } ãªãã¡ã¯ã¿ãªã³ã° derive 颿°ãè¥å€§åããŠããã®ã§å
éšã®åŠçã颿°ãšããŠåãåºããŸãããäž»ãªå€æŽç¹ã¯ä»¥äžã®éãã§ãã Builder æ§é äœã®å®çŸ©ãçæããéšåã颿°åïŒ build_builder_struct ïŒ Builder æ§é äœã®å®è£
ïŒsetter 颿°ã build 颿°ïŒãçæããéšåã颿°åïŒ build_builder_impl ïŒ builder 颿°ãçæããéšåã颿°åïŒ build_struct_impl ïŒ ãããã®é¢æ°ã¯ãã¹ãŠæ»ãå€ãšã㊠proc_macro2::TokenStream ãè¿ããŠããŸããããã㯠quote ãã¯ãã proc_macro2::TokenStream ãè¿ãããã§ãã ãããã®åŠçã颿°åããã®ã«äŒŽããããšããšä»¥äžã®ããã«ãã£ãŒã«ãåãšåãæåã«ååŸããŠããã®ãã let (idents, types): ( Vec < Ident > , Vec < Type > ) = match input.data { Data :: Struct (data) => match data.fields { Fields :: Named (fields) => fields .named . into_iter () . map ( | field | { let ident = field.ident; let ty = field.ty; (ident. unwrap (), ty) }) . unzip (), _ => panic! ( "no unnamed fields are allowed" ), }, _ => panic! ( "expects struct" ), }; 以äžã®ããã« NamedFields ãååŸããŠå颿°ã«ãããããã«å€æŽããŠããŸãã let fields = match input.data { Data :: Struct (data) => match data.fields { Fields :: Named (fields) => fields, _ => panic! ( "no unnamed fields are allowed" ), }, _ => panic! ( "this macro can be applied only to structaa" ), }; ãªãã¡ã¯ã¿ãªã³ã°åŸã®å®è£
ã¯ä»¥äžã®éãã§ãã use proc_macro2 :: TokenStream; use quote :: {format_ident, quote}; use syn :: { parse_macro_input, Data, DeriveInput, Fields, FieldsNamed, GenericArgument, Ident, PathArguments, PathSegment, Type, Visibility, }; #[proc_macro_derive(Builder, attributes(builder))] pub fn derive (input: proc_macro :: TokenStream) -> proc_macro :: TokenStream { let input = parse_macro_input! (input as DeriveInput); let ident = input.ident; let vis = input.vis; let builder_name = format_ident! ( "{}Builder" , ident); let fields = match input.data { Data :: Struct (data) => match data.fields { Fields :: Named (fields) => fields, _ => panic! ( "no unnamed fields are allowed" ), }, _ => panic! ( "this macro can be applied only to structaa" ), }; let builder_struct = build_builder_struct ( & fields, & builder_name, & vis); let builder_impl = build_builder_impl ( & fields, & builder_name, & ident); let struct_impl = build_struct_impl ( & fields, & builder_name, & ident); let expand = quote! { #builder_struct #builder_impl #struct_impl }; proc_macro :: TokenStream :: from (expand) } fn build_builder_struct ( fields: & FieldsNamed, builder_name: & Ident, visibility: & Visibility, ) -> TokenStream { let (idents, types): ( Vec < & Ident > , Vec < & Type > ) = fields .named . iter () . map ( | field | { let ident = field.ident. as_ref (); let ty = unwrap_option ( & field.ty). unwrap_or ( & field.ty); (ident. unwrap (), ty) }) . unzip (); quote! { #visibility struct #builder_name { #( #idents : Option < #types > ), * } } } fn build_builder_impl ( fields: & FieldsNamed, builder_name: & Ident, struct_name: & Ident, ) -> TokenStream { let checks = fields .named . iter () . filter ( | field | ! is_option ( & field.ty)) . map ( | field | { let ident = field.ident. as_ref (); let err = format! ( "Required field '{}' is missing" , ident. unwrap (). to_string ()); quote! { if self . #ident . is_none () { return Err ( #err . into ()); } } }); let setters = fields.named. iter (). map ( | field | { let ident = & field.ident; let ty = unwrap_option ( & field.ty). unwrap_or ( & field.ty); quote! { pub fn #ident ( &mut self , #ident : #ty ) -> &mut Self { self . #ident = Some ( #ident ); self } } }); let struct_fields = fields.named. iter (). map ( | field | { let ident = field.ident. as_ref (); if is_option ( & field.ty) { quote! { #ident : self . #ident . clone () } } else { quote! { #ident : self . #ident . clone (). unwrap () } } }); quote! { impl #builder_name { #( #setters ) * pub fn build ( &mut self ) -> Result < #struct_name , Box < dyn std :: error :: Error >> { #( #checks ) * Ok ( #struct_name { #( #struct_fields ), * }) } } } } fn build_struct_impl ( fields: & FieldsNamed, builder_name: & Ident, struct_name: & Ident, ) -> TokenStream { let field_defaults = fields.named. iter (). map ( | field | { let ident = field.ident. as_ref (); quote! { #ident : None } }); quote! { impl #struct_name { pub fn builder () -> #builder_name { #builder_name { #( #field_defaults ), * } } } } } fn is_option (ty: & Type) -> bool { match get_last_path_segment (ty) { Some (seg) => seg.ident == "Option" , _ => false , } } fn unwrap_option (ty: & Type) -> Option < & Type > { if ! is_option (ty) { return None ; } match get_last_path_segment (ty) { Some (seg) => match seg.arguments { PathArguments :: AngleBracketed ( ref args) => { args.args. first (). and_then ( | arg | match arg { & GenericArgument :: Type ( ref ty) => Some (ty), _ => None , }) } _ => None , }, None => None , } } fn get_last_path_segment (ty: & Type) -> Option < & PathSegment > { match ty { Type :: Path (path) => path.path.segments. last (), _ => None , } } 07-repeated-field ç®æš 以äžã® 2 ã€ã®æ¹æ³ã§ãã¯ã¿ãå€ãšããŠãã€ãã£ãŒã«ããæŽæ°ã§ããããã«ãã ãã¯ã¿ãäžããŠäžæ¬ã§æŽæ°ãã ãã¯ã¿ã®èŠçŽ ã 1 ã€ãã€è¿œå ãã äžæ¬ã§æŽæ°ããããã®é¢æ°åã¯ãã£ãŒã«ãåãšåãã«ãã ãã¯ã¿ã®èŠçŽ ã 1 ã€ãã€è¿œå ãã颿°ã®ååã¯ä»¥äžã®ããã«ã¢ããªãã¥ãŒããçšããŠæå®ãã 1 ã€ãã€è¿œå ããããã®é¢æ°åãšããŠäžæ¬ã§æŽæ°ããããã®é¢æ°åãšåãååãæå®ãããå Žå㯠1 ã€ãã€è¿œå ããããã®é¢æ°ãåªå
ãã #[derive(Builder)] pub struct Command { executable: String , #[builder(each = "arg" )] args: Vec < String > , #[builder(each = "env" )] env: Vec < String > , current_dir: Option < String > , } å®è£
æ¹é ç®æšãšããæ©èœãå®çŸããããã«å®è£
ããå¿
èŠãããã®ã¯ä»¥äžã®é
ç®ã§ãã ãã£ãŒã«ãã«ä»äžãããã¢ããªãã¥ãŒããååŸãã builder ã¢ããªãã¥ãŒããä»äžã§ããããã«ãã èŠçŽ ã 1 ã€ãã€è¿œå ãã颿°å㯠build ã¢ããªãã¥ãŒãã® each ããŒã«æå®ãã ã¢ããªãã¥ãŒãã®ããŒåïŒ each ïŒã®ããªããŒã·ã§ã³ã¯æ¬¡ã®ã¹ãããã§å®è£
ãã Vec åã®ãã£ãŒã«ãã® setter ãäžæ¬æŽæ°çšãšèŠçŽ è¿œå çšã® 2 çš®é¡çæãã ãŸããèŠçŽ ã 1 ã€ãã€è¿œå ã§ããããã«ããããã«ã¯ä»¥äžã®æ©èœã®å®è£
ãå¿
èŠã§ãã Builder æ§é äœã®ãã£ãŒã«ãã§ã¯ Vec åã®å€æ°ã¯ Option ã§ã©ããããªã ãã£ãŒã«ãã®åã Vec ãã©ããå€å®ã§ããããã«ãã Vec ãã¢ã³ã©ããããŠäžèº«ã®åãååŸã§ããããã«ãã å®è£
Builder æ§é äœã®ãã£ãŒã«ãã§ã¯ Vec åã®å€æ°ã¯ Option ã§ã©ããããªã Builder æ§é äœã®å®çŸ©ãçæããŠããã®ã¯ build_builder_struct 颿°ã§ããä»ã®å®è£
ã§ã¯å
¥åã®åã«é¢ããã Option ã§ã©ããããŠããŸããããã Vec ã®ã¿ã©ããããªãããã«å€æŽããã°è¯ãããã§ãã fn build_builder_struct ( fields: & FieldsNamed, builder_name: & Ident, visibility: & Visibility, ) -> TokenStream { let (idents, types): ( Vec < & Ident > , Vec < & Type > ) = fields .named . iter () . map ( | field | { let ident = field.ident. as_ref (); let ty = unwrap_option ( & field.ty). unwrap_or ( & field.ty); (ident. unwrap (), ty) }) . unzip (); quote! { #visibility struct #builder_name { #( #idents : Option < #types > ), * } } } is_vector 颿°ã¯ is_option ãšäŒŒã颿°ã§ãäžããããåã Vec åãã©ãããå€å®ãã颿°ã§ããå®è£
ã¯åŸè¿°ããŸãã fn build_builder_struct ( fields: & FieldsNamed, builder_name: & Ident, visibility: & Visibility, ) -> TokenStream { let struct_fields = fields .named . iter () . map ( | field | { let ident = field.ident. as_ref (); let ty = unwrap_option ( & field.ty). unwrap_or ( & field.ty); (ident. unwrap (), ty) }) . map ( | (ident, ty) | { if is_vector ( & ty) { quote! { #ident : #ty } } else { quote! { #ident : Option < #ty > } } }); quote! { #visibility struct #builder_name { #( #struct_fields ), * } } } Builder æ§é äœã®å®çŸ©ãå€ãã£ãã®ã§ãBuilder æ§é äœãè¿ã builder 颿°ã®å®è£
ãçæãã build_struct_impl 颿°ãä¿®æ£ãå¿
èŠã§ãã Vec åã®ãã£ãŒã«ãã®ã¿ Vec::new() ãè¿ãããã«ããã°è¯ãããã§ãã fn build_struct_impl ( fields: & FieldsNamed, builder_name: & Ident, struct_name: & Ident, ) -> TokenStream { let field_defaults = fields.named. iter (). map ( | field | { let ident = field.ident. as_ref (); quote! { #ident : None } }); quote! { impl #struct_name { pub fn builder () -> #builder_name { #builder_name { #( #field_defaults ), * } } } } } ãã¡ãã build_builder_struct 颿°ãšåæ§ã is_vector 颿°ã䜿ã£ãŠæ¡ä»¶åå²ãèšè¿°ããŠããŸãã fn build_struct_impl ( fields: & FieldsNamed, builder_name: & Ident, struct_name: & Ident, ) -> TokenStream { let field_defaults = fields.named. iter (). map ( | field | { let ident = field.ident. as_ref (); let ty = & field.ty; if is_vector ( & ty) { quote! { #ident : Vec :: new () } } else { quote! { #ident : None } } }); quote! { impl #struct_name { pub fn builder () -> #builder_name { #builder_name { #( #field_defaults ), * } } } } } ãŸãã Vec åã®ãã£ãŒã«ãã¯èŠçŽ ãå«ãŸãªããŠãåé¡ãªãã®ã§ã Vec åã®ãã£ãŒã«ãã«ã€ããŠãã¬ãŒãç¯ãçæããªãããã«å€æŽããŸããä»ã¯ Option ã®ã¿ããã£ã«ã¿ããŠããŸããã远å ã§ Vec ããã£ã«ã¿ããããã«å€æŽããŸãã let checks = fields .named . iter () . filter ( | field | ! is_option ( & field.ty)) . filter ( | field | ! is_vector ( & field.ty)) . map ( | field | { let ident = field.ident. as_ref (); let err = format! ( "Required field '{}' is missing" , ident. unwrap (). to_string ()); quote! { if self . #ident . is_none () { return Err ( #err . into ()); } } }); is_vector ãš unwrap_vector ã®å®è£
ãããã㯠is_vector ãš unwrap_vector ãå®è£
ããŠãããŸãããããŸã§ã®å®è£
ã§ã¯ unwrap_vector ã¯ã§ãŠããŸããããä»åŸäœ¿ãã®ã§ããã§å®è£
ããŠãããŸãã Vec ã Option ãšåæ§ Type::Path ã«åé¡ãããã®ã§ã以äžã®é
ç®ã¯ 06 ã§å®è£
ãã is_option ã unwrap_option ãæµçšã§ããŸãã fn is_vector (ty: & Type) -> bool { match get_last_path_segment (ty) { Some (seg) => seg.ident == "Vec" , _ => false , } } fn unwrap_vector (ty: & Type) -> Option < & Type > { if ! is_vector (ty) { return None ; } match get_last_path_segment (ty) { Some (seg) => match seg.arguments { PathArguments :: AngleBracketed ( ref args) => { args.args. first (). and_then ( | arg | match arg { & GenericArgument :: Type ( ref ty) => Some (ty), _ => None , }) } _ => None , }, None => None , } } unwrap_vector ã®ãã¿ãŒã³ãããã®éšå㯠unwrap_option ãšåæ§ã®åŠçãããŠããã®ã§é¢æ°åã§ãããã§ããããã unwrap_generic_type ãšãã颿°ã«ãããåºããšä»¥äžã®ããã«ãªããŸãã fn unwrap_option (ty: & Type) -> Option < & Type > { if ! is_option (ty) { return None ; } unwrap_generic_type (ty) } fn unwrap_vector (ty: & Type) -> Option < & Type > { if ! is_vector (ty) { return None ; } unwrap_generic_type (ty) } fn unwrap_generic_type (ty: & Type) -> Option < & Type > { match get_last_path_segment (ty) { Some (seg) => match seg.arguments { PathArguments :: AngleBracketed ( ref args) => { args.args. first (). and_then ( | arg | match arg { & GenericArgument :: Type ( ref ty) => Some (ty), _ => None , }) } _ => None , }, None => None , } } ãã£ãŒã«ãã«ä»äžãããã¢ããªãã¥ãŒããååŸãã ãã£ãŒã«ãã«ä»äžãããã¢ããªãã¥ãŒããååŸããåŠçãå®è£
ããåã«ããŸãã¢ããªãã¥ãŒããä»äžã§ããããã«ããå¿
èŠããããŸãã ã¢ããªãã¥ãŒããä»äžã§ããããã«ããããã«ã¯ã以äžã®ããã« derive 颿°ã« attributes(builder) ãšããã¢ããªãã¥ãŒãã远å ããŸãïŒ åè ïŒãããã§ãã£ãŒã«ãã« #[builder(...)] ã®ãããªã¢ããªãã¥ãŒããä»äžã§ããŸãã #[proc_macro_derive(Builder, attributes(builder))] pub fn derive (input: proc_macro :: TokenStream) -> proc_macro :: TokenStream { ã¢ããªãã¥ãŒããä»äžã§ããããã«ãªã£ãã®ã§ããããååŸããåŠçãå®è£
ããŸãã ã¢ããªãã¥ãŒããååŸããã«ã¯ã©ã®ããŒã¿ãåŠçããã°è¯ãã§ããããããŸã㯠DeriveInput ãã¿ãŠã¿ãŸãããã pub struct DeriveInput { pub attrs: Vec < Attribute > , pub vis: Visibility, pub ident: Ident, pub generics: Generics, pub data: Data, } DeriveInput 㯠attrs ãã£ãŒã«ããæã£ãŠããŸããã以äžã® DeriveInput ã® attrs ã®èª¬æã«ããããã«ãããã¯æ§é äœèªäœã«ä»äžãããã¢ããªãã¥ãŒãã§ããïŒ åŒçšå
ïŒ Attributes tagged on the whole struct or enum. æ§é äœã®ãã£ãŒã«ã㯠data ãã£ãŒã«ãã«æ ŒçŽãããŠããã®ã§ Data ã®å®çŸ©ãã¿ãŠã¿ãŸãããã Data ã®æ§é ãäžã£ãŠãããšæçµçã«æ§é äœã®åãã£ãŒã«ãã®æ
å ±ãä¿æããŠãã Field æ§é äœãåŸãããŸãã Data ã®æ§é ã®è©³çްã«ã€ããŠã¯ åç·š ãåèã«ããŠãã ããããã®äžã«ãã attrs ããã£ãŒã«ãã«ä»äžãããã¢ããªãã¥ãŒãã§ãã pub struct Field { pub attrs: Vec < Attribute > , pub vis: Visibility, pub ident: Option < Ident > , pub colon_token: Option < Colon > , pub ty: Type, } æ§é äœã®ãã£ãŒã«ã㯠derive 颿°ã®æåã®æ¹ã§ååŸããŠããã®ã§ããããåŠçããŠã¢ããªãã¥ãŒããååŸããŠãããŸãã attrs 㯠Attribute ã®ãã¯ã¿ã«ãªã£ãŠããŸãããä»å㯠1 ã€ã®ã¢ããªãã¥ãŒããã䜿ããªãã®ã§ first ã§å
é ã®ã¢ããªãã¥ãŒãã ãååŸããã°è¯ãã§ãããã let ident_each_name = field .attrs . first () . map ( | attr | todo! ()); Attribute ã® parse_meta 颿° ã§ã¢ããªãã¥ãŒããããŒã¹ããçµæãåŸãããŸãã let ident_each_name = field .attrs . first () . map ( | attr | attr. parse_meta ()); parse_meta() 颿°ã®æ»ãå€ã¯ Result<Meta> ã§ãã Meta åã¯ä»¥äžã®ãã㪠enum ã§ãã pub enum Meta { Path (Path), List (MetaList), NameValue (MetaNameValue), } Meta ã® List ããªã¢ã³ã㮠説æ ã« List A meta list is like the derive(Copy) in #[derive(Copy)] . ãšããããã«ãä»åååŸãããã®ã¯ Meta::List ãªã®ã§ãã¿ãŒã³ãããã§åŠçããŸãã let ident_each_name = field .attrs . first () . map ( | attr | match attr. parse_meta () { Ok ( Meta :: List (_)) => todo! (), _ => None , }); MetaList ã¯ä»¥äžã®ãããªæ§é äœã§ãã pub struct MetaList { pub path: Path, pub paren_token: Paren, pub nested: Punctuated < NestedMeta, Comma > , } MetaList åã® path ã¯ã¢ããªãã¥ãŒãåïŒ #[builder(each="foo")] ã® builder ã®éšåïŒãã nested ã¢ããªãã¥ãŒãã®å€ïŒ #[builder(each="foo")] ã® each="foo" ã®éšåïŒãä¿æããŠããŸããä»å¿
èŠãªã®ã¯ã¢ããªãã¥ãŒãã®å€ãä¿æãã nested ã®éšåã§ãã nested ã¯ã¢ããªãã¥ãŒãå€ãè€æ°ä¿æããŠããŸãããä»åã¯è€æ°ã®å€ããã€ããšã¯æ³å®ããŠããªãã®ã§ first ã§å
é ãååŸããã°è¯ãããã§ãã let ident_each_name = field .attrs . first () . map ( | attr | match attr. parse_meta () { Ok ( Meta :: List (list)) => list.nested. first (), _ => None , }); nested.first() 㯠Option<NestedMeta> ãè¿ããŸãã NestedMeta ã¯ä»¥äžã®ãã㪠enum ã§ãã pub enum NestedMeta { Meta (Meta), Lit (Lit), } NestedMeta ã®ãã£ãŒã«ãã«é¢ãã 以äžã®èšè¿° ãããããããã«ã Lit 㯠Rust ã®ãªãã©ã«ãä¿æããŸãããã®æ®µéã§ã¯ nested.first() ãã Some(each="foo") ã®ãããªåœ¢åŒãè¿ã£ãŠããããšãæåŸ
ããŠããã®ã§ Lit ã§ã¯ãªã Meta ã«ãããããããã«ããŸãã Meta(Meta) A structured meta item, like the Copy in #[derive(Copy)] which would be a nested Meta::Path. Lit(Lit) A Rust literal, like the "new_name" in #[rename("new_name")]. let ident_each_name = field .attrs . first () . map ( | attr | match attr. parse_meta () { Ok ( Meta :: List (list)) => match list.nested. first () { Some ( NestedMeta :: Meta (_)) => todo! (), _ => None , }, _ => None , }); å
ã»ã©ã¯ãã¿ãŒã³ããããçšã㊠Meta::List ãååŸããŸããããäžè¿°ã®ããã«ä»åºŠã¯ each="foo" ã®ãããªããŒãšããªã¥ãŒã®ãã¢ãååŸãããããšãæåŸ
ããŠããã®ã§ã Meta::NameValue(MetaNameValue) ããããããŠåŠçããŸãã let ident_each_name = field .attrs . first () . map ( | attr | match attr. parse_meta () { Ok ( Meta :: List (list)) => match list.nested. first () { Some ( NestedMeta :: Meta ( Meta :: NameValue (MetaNameValue{}))) => todo! (), _ => None , }, _ => None , }); MetaNameValue ã¯ä»¥äžã®ãããªæ§é äœã§ãã pub struct MetaNameValue { pub path: Path, pub eq_token: Eq , pub lit: Lit, } each = "foo" ãäŸã«ãšããšã MetaNameValue 㯠path ã« each ãã lit ã« "foo" ãæ ŒçŽããŸããä»å欲ããã®ã¯ "foo" ã®æ¹ãªã®ã§ lit ã ããååŸããã°è¯ãããã§ãã let ident_each_name = field .attrs . first () . map ( | attr | match attr. parse_meta () { Ok ( Meta :: List (list)) => match list.nested. first () { Some ( NestedMeta :: Meta ( Meta :: NameValue (MetaNameValue{ path: _, eq_token: _, lit }))) => todo! (), _ => None , }, _ => None , }); Lit ã¯ä»¥äžã®ãã㪠enum ã§ãã each = "foo" ã®ãããªåœ¢åŒããããããããã«ãæååãªãã©ã«ïŒ Lit::Str ïŒãåŸãããããšãæåŸ
ããŠããŸãã pub enum Lit { Str (LitStr), ByteStr (LitByteStr), Byte (LitByte), Char (LitChar), Int (LitInt), Float (LitFloat), Bool (LitBool), Verbatim (Literal), } ãªãã©ã«ã衚ãå€ã¯ value ã¡ãœããã§ååŸã§ããã®ã§ã let ident_each_name = field .attrs . first () . map ( | attr | match attr. parse_meta () { Ok ( Meta :: List (list)) => match list.nested. first () { Some ( NestedMeta :: Meta ( Meta :: NameValue (MetaNameValue{ path: _, eq_token: _, lit: Lit :: Str ( ref s) }))) => { Some (s. value ()) }, _ => None , }, _ => None , }) . flatten (); 以äžã§åãã£ãŒã«ãã®èŠçŽ è¿œå çšã®ã¡ãœããåãååŸã§ããŸããã Vec åã®ãã£ãŒã«ãã® setter ãäžæ¬æŽæ°çšãšèŠçŽ è¿œå çšã® 2 çš®é¡çæãã ä»ãŸã§ã¯ãã£ãŒã«ãã®åã«ããããåçŽã«ä»¥äžã®ããã« setter ãçæããŠããŸããã quote! { pub fn #ident ( &mut self , #ident : #t ) -> &mut Self { self . #ident = Some ( #ident ); self } } ãŸãã¢ããªãã¥ãŒããä»äžãããŠãããã©ããã§åå²ãçºçããŸããã¢ããªãã¥ãŒããä»äžãããŠãããšèŠçŽ è¿œå çšã®ã¡ãœãããå¿
èŠã«ãªããŸãã match ident_each_name { Some (name) => todo! (), None => todo! (), } ãŸãã¯èŠçŽ è¿œå çšã®ã¡ãœããããããªãæ¹ãå®è£
ããŸãã Vec åã®ãã£ãŒã«ã㯠Option ã§ã©ãããããªãã®ã§ Vec åãã©ããã§ setter ã®å®è£
ãå€ãããŸãã match ident_each_name { Some (name) => todo! (), None => { if is_vector ( & ty) { quote! { pub fn #ident ( &mut self , #ident : #ty ) -> &mut Self { self . #ident = #ident ; self } } } else { quote! { pub fn #ident ( &mut self , #ident : #ty ) -> &mut Self { self . #ident = Some ( #ident ); self } } } }, } 次ã«èŠçŽ è¿œå çšã®ã¡ãœãããå¿
èŠãªãã¿ãŒã³ãå®è£
ããŸãããã£ã¡ã¯ä»¥äžã® 2 ã€ã®ãã¿ãŒã³ã§åŠçãåå²ããŸãã èŠçŽ è¿œå çšã®ã¡ãœããåããã£ãŒã«ãåãš åã èŠçŽ è¿œå çšã®ã¡ãœããã®ã¿çæãã èŠçŽ è¿œå çšã®ã¡ãœããåããã£ãŒã«ãåãš ç°ãªã èŠçŽ è¿œå çšã®ã¡ãœãããšäžæ¬æŽæ°çšã®ã¡ãœãããäž¡æ¹çæãã å®è£
ã¯ä»¥äžã®ããã«ãªããŸããå®è£
ã®ãã€ã³ãã¯ä»¥äžã®éãã§ãã èŠçŽ è¿œå çšã®é¢æ°ã®åŒæ°ã®åãšããŠäœ¿çšããããã« unwrap_vector ã§äžèº«ã®åãåãåºã èŠçŽ è¿œå çšã®é¢æ°ã®ååïŒ name ïŒã¯ String ãªã®ã§ Ident::new ã§ Ident ãçæãã Ident::new ã®ç¬¬äºåŒæ°ã«ã¯ Span æ§é äœãæå®ããå¿
èŠãããã Span æ§é äœã¯ãã¯ãã®å±éå
ã§èå¥åã誀ã£ãŠææãããªãããã«ããããã«å¿
èŠïŒ åè ïŒ match ident_each_name { Some (name) => { let ty_each = unwrap_vector (ty). unwrap (); let ident_each = Ident :: new (name. as_str (), Span :: call_site ()); if ident. unwrap (). to_string () == name { quote! { pub fn #ident_each ( &mut self , #ident_each : #ty_each ) -> &mut Self { self . #ident . push ( #ident_each ); self } } } else { quote! { pub fn #ident ( &mut self , #ident : #ty ) -> &mut Self { self . #ident = #ident ; self } pub fn #ident_each ( &mut self , #ident_each : #ty_each ) -> &mut Self { self . #ident . push ( #ident_each ); self } } } } None => { (ç¥) }, } æçµçãªå®è£
ã¯ä»¥äžã®ããã«ãªããŸãã use proc_macro2 :: {Span, TokenStream}; use quote :: {format_ident, quote}; use syn :: { parse_macro_input, Data, DeriveInput, Fields, FieldsNamed, GenericArgument, Ident, Lit, Meta, MetaList, MetaNameValue, NestedMeta, PathArguments, PathSegment, Type, Visibility, }; #[proc_macro_derive(Builder, attributes(builder))] pub fn derive (input: proc_macro :: TokenStream) -> proc_macro :: TokenStream { let input = parse_macro_input! (input as DeriveInput); let ident = input.ident; let vis = input.vis; let builder_name = format_ident! ( "{}Builder" , ident); let fields = match input.data { Data :: Struct (data) => match data.fields { Fields :: Named (fields) => fields, _ => panic! ( "no unnamed fields are allowed" ), }, _ => panic! ( "this macro can be applied only to struct" ), }; let builder_struct = build_builder_struct ( & fields, & builder_name, & vis); let builder_impl = build_builder_impl ( & fields, & builder_name, & ident); let struct_impl = build_struct_impl ( & fields, & builder_name, & ident); let expand = quote! { #builder_struct #builder_impl #struct_impl }; proc_macro :: TokenStream :: from (expand) } fn build_builder_struct ( fields: & FieldsNamed, builder_name: & Ident, visibility: & Visibility, ) -> TokenStream { let struct_fields = fields .named . iter () . map ( | field | { let ident = field.ident. as_ref (); let ty = unwrap_option ( & field.ty). unwrap_or ( & field.ty); (ident. unwrap (), ty) }) . map ( | (ident, ty) | { if is_vector ( & ty) { quote! { #ident : #ty } } else { quote! { #ident : Option < #ty > } } }); quote! { #visibility struct #builder_name { #( #struct_fields ), * } } } fn build_builder_impl ( fields: & FieldsNamed, builder_name: & Ident, struct_name: & Ident, ) -> TokenStream { let checks = fields .named . iter () . filter ( | field | ! is_option ( & field.ty)) . filter ( | field | ! is_vector ( & field.ty)) . map ( | field | { let ident = field.ident. as_ref (); let err = format! ( "Required field '{}' is missing" , ident. unwrap (). to_string ()); quote! { if self . #ident . is_none () { return Err ( #err . into ()); } } }); let setters = fields.named. iter (). map ( | field | { let ident_each_name = field .attrs . first () . map ( | attr | match attr. parse_meta () { Ok ( Meta :: List (list)) => match list.nested. first () { Some ( NestedMeta :: Meta ( Meta :: NameValue (MetaNameValue { path: _, eq_token: _, lit: Lit :: Str ( ref str ), }))) => Some ( str . value ()), _ => None , }, _ => None , }) . flatten (); let ident = field.ident. as_ref (); let ty = unwrap_option ( & field.ty). unwrap_or ( & field.ty); match ident_each_name { Some (name) => { let ty_each = unwrap_vector (ty). unwrap (); let ident_each = Ident :: new (name. as_str (), Span :: call_site ()); if ident. unwrap (). to_string () == name { quote! { pub fn #ident_each ( &mut self , #ident_each : #ty_each ) -> &mut Self { self . #ident . push ( #ident_each ); self } } } else { quote! { pub fn #ident ( &mut self , #ident : #ty ) -> &mut Self { self . #ident = #ident ; self } pub fn #ident_each ( &mut self , #ident_each : #ty_each ) -> &mut Self { self . #ident . push ( #ident_each ); self } } } } None => { if is_vector ( & ty) { quote! { pub fn #ident ( &mut self , #ident : #ty ) -> &mut Self { self . #ident = #ident ; self } } } else { quote! { pub fn #ident ( &mut self , #ident : #ty ) -> &mut Self { self . #ident = Some ( #ident ); self } } } } } }); let struct_fields = fields.named. iter (). map ( | field | { let ident = field.ident. as_ref (); if is_option ( & field.ty) || is_vector ( & field.ty) { quote! { #ident : self . #ident . clone () } } else { quote! { #ident : self . #ident . clone (). unwrap () } } }); quote! { impl #builder_name { #( #setters ) * pub fn build ( &mut self ) -> Result < #struct_name , Box < dyn std :: error :: Error >> { #( #checks ) * Ok ( #struct_name { #( #struct_fields ), * }) } } } } fn build_struct_impl ( fields: & FieldsNamed, builder_name: & Ident, struct_name: & Ident, ) -> TokenStream { let field_defaults = fields.named. iter (). map ( | field | { let ident = field.ident. as_ref (); let ty = & field.ty; if is_vector ( & ty) { quote! { #ident : Vec :: new () } } else { quote! { #ident : None } } }); quote! { impl #struct_name { pub fn builder () -> #builder_name { #builder_name { #( #field_defaults ), * } } } } } fn is_option (ty: & Type) -> bool { match get_last_path_segment (ty) { Some (seg) => seg.ident == "Option" , _ => false , } } fn is_vector (ty: & Type) -> bool { match get_last_path_segment (ty) { Some (seg) => seg.ident == "Vec" , _ => false , } } fn unwrap_option (ty: & Type) -> Option < & Type > { if ! is_option (ty) { return None ; } unwrap_generic_type (ty) } fn unwrap_vector (ty: & Type) -> Option < & Type > { if ! is_vector (ty) { return None ; } unwrap_generic_type (ty) } fn unwrap_generic_type (ty: & Type) -> Option < & Type > { match get_last_path_segment (ty) { Some (seg) => match seg.arguments { PathArguments :: AngleBracketed ( ref args) => { args.args. first (). and_then ( | arg | match arg { & GenericArgument :: Type ( ref ty) => Some (ty), _ => None , }) } _ => None , }, None => None , } } fn get_last_path_segment (ty: & Type) -> Option < & PathSegment > { match ty { Type :: Path (path) => path.path.segments. last (), _ => None , } } 08-unrecognized-attribute ç®æš attribute ã«ééã£ãèå¥åãäžããããéã«é©åãªã³ã³ãã€ã«ãšã©ãŒã衚瀺ãã å®è£
æ¹é ã¢ããªãã¥ãŒãã®ããŒãšã㊠each 以å€ã®ãã®ãäžããããå Žåã«ãšã©ãŒã衚瀺ãã ãšã©ãŒãçºçããããç®æã§ syn::Error ã® to_compile_error ã¡ãœããã§ TokenStream ãè¿ãããã«ãã åçŽã« panic! ãããã ãããã詳现ãªãšã©ãŒã¡ãã»ãŒãžã衚瀺ããããã å®è£
ã¢ããªãã¥ãŒãã®ããŒãæ£ãããå€å®ããå¿
èŠãããã®ã§ãã¢ããªãã¥ãŒããåŠçããŠãã以äžã®ç®æã倿ŽããŸãã let ident_each_name = field .attrs . first () . map ( | attr | match attr. parse_meta () { Ok ( Meta :: List (list)) => match list.nested. first () { Some ( NestedMeta :: Meta ( Meta :: NameValue (MetaNameValue { path: _, eq_token: _, lit: Lit :: Str ( ref str ), }))) => Some ( str . value ()), _ => None , }, _ => None , }) . flatten (); ã¢ããªãã¥ãŒãã®ããŒã¯ MetadataNameValue ã® path ã«æ ŒçŽãããŠããŸãã path 㯠Path åãªã®ã§ 06-optional-field ã§åŠçããã®ãšåæ§ã«ããŠèå¥åãååŸããŸãã Ident ã® to_string ã¡ãœããã§èå¥åã®ååãååŸã§ããã®ã§ãããã each ãšäžèŽããªããã°ãšã©ãŒãè¿ãã°è¯ãããã§ãã let ident_each_name = field .attrs . first () . map ( | attr | match attr. parse_meta () { Ok ( Meta :: List (list)) => match list.nested. first () { Some ( NestedMeta :: Meta ( Meta :: NameValue (MetaNameValue { ref path, eq_token: _, lit: Lit :: Str ( ref str ), }))) => { if let Some (name) = path.segments. first () { if name.ident. to_string () != "each" { todo! () } } Some ( str . value ()) } _ => None , }, _ => None , }) . flatten (); syn::Error 㯠syn::Error::new_spanned() ã䜿ã£ãŠçæããŸãããšã©ãŒã¡ãã»ãŒãžïŒ"expected ..."ïŒã¯ãã¹ãã±ãŒã¹ã«èšèŒãããã¡ãã»ãŒãžããã®ãŸãŸäœ¿ããŸãã以äžã®ããã«ããããšããã§ããããã®ãŸãŸã§ã¯åãåããªãã®ã§ã³ã³ãã€ã«ã§ããŸããã let ident_each_name = field .attrs . first () . map ( | attr | match attr. parse_meta () { Ok ( Meta :: List (list)) => match list.nested. first () { Some ( NestedMeta :: Meta ( Meta :: NameValue (MetaNameValue { ref path, eq_token: _, lit: Lit :: Str ( ref str ), }))) => { if let Some (name) = path.segments. first () { if name.ident. to_string () != "each" { return Some ( syn :: Error :: new_spanned ( list, "expected `builder(each = \" ... \" )`" , )); } } Some ( str . value ()) } _ => None , }, _ => None , }) . flatten (); ããã§ã以äžã®ãã㪠enum ãè¿ãããã«ããŠãåŸã§ãã¿ãŒã³ãããã§åŠçããŸãããã enum LitOrError { Lit ( String ), Error ( syn :: Error), } LitOrError ã䜿ã£ãŠå
ã»ã©ã®ç®æã次ã®ããã«æžãæããŸãã let ident_each_name = field .attrs . first () . map ( | attr | match attr. parse_meta () { Ok ( Meta :: List (list)) => match list.nested. first () { Some ( NestedMeta :: Meta ( Meta :: NameValue (MetaNameValue { ref path, eq_token: _, lit: Lit :: Str ( ref str ), }))) => { if let Some (name) = path.segments. first () { if name.ident. to_string () != "each" { return Some ( LitOrError :: Error ( syn :: Error :: new_spanned ( list, "expected `builder(each = \" ... \" )`" , ))); } } Some ( LitOrError :: Lit ( str . value ())) } _ => None , }, _ => None , }) . flatten (); ãã¿ãŒã³ãããã§åŠçããŠããéšåã enum ã«åãããŠå€æŽããŸãã LitOrError::Error ã«ãããããå Žåã¯ã³ã³ãã€ã«ãšã©ãŒãçããããå¿
èŠãããã®ã§ã to_compile_error().into() ã§ãšã©ãŒãè¿ããŸãã match ident_each_name { Some ( LitOrError :: Lit (name)) => { (ç¥) } Some ( LitOrError :: Error (err)) => err. to_compile_error (). into (), None => { (ç¥) } } ä»ãŸã§ panic! ããŠããå Žæã syn::Error ã® to_compile_error ã¡ãœããã䜿ã£ãŠ TokenStream ãè¿ãããã«å€æŽããŠãããŸãã let fields = match input.data { Data :: Struct (data) => match data.fields { Fields :: Named (fields) => fields, _ => { return syn :: Error :: new (ident. span (), "expects named fields" ) . to_compile_error () . into () } }, _ => { return syn :: Error :: new (ident. span (), "expects struct" ) . to_compile_error () . into () } }; æçµçãªå®è£
ã¯ä»¥äžã®ããã«ãªããŸãã use proc_macro2 :: {Span, TokenStream}; use quote :: {format_ident, quote}; use syn :: { parse_macro_input, Data, DeriveInput, Fields, FieldsNamed, GenericArgument, Ident, Lit, Meta, MetaNameValue, NestedMeta, PathArguments, PathSegment, Type, Visibility, }; enum LitOrError { Lit ( String ), Error ( syn :: Error), } #[proc_macro_derive(Builder, attributes(builder))] pub fn derive (input: proc_macro :: TokenStream) -> proc_macro :: TokenStream { let input = parse_macro_input! (input as DeriveInput); let ident = input.ident; let vis = input.vis; let builder_name = format_ident! ( "{}Builder" , ident); let fields = match input.data { Data :: Struct (data) => match data.fields { Fields :: Named (fields) => fields, _ => { return syn :: Error :: new (ident. span (), "expects named fields" ) . to_compile_error () . into () } }, _ => { return syn :: Error :: new (ident. span (), "expects struct" ) . to_compile_error () . into () } }; let builder_struct = build_builder_struct ( & fields, & builder_name, & vis); let builder_impl = build_builder_impl ( & fields, & builder_name, & ident); let struct_impl = build_struct_impl ( & fields, & builder_name, & ident); let expand = quote! { #builder_struct #builder_impl #struct_impl }; proc_macro :: TokenStream :: from (expand) } fn build_builder_struct ( fields: & FieldsNamed, builder_name: & Ident, visibility: & Visibility, ) -> TokenStream { let struct_fields = fields .named . iter () . map ( | field | { let ident = field.ident. as_ref (); let ty = unwrap_option ( & field.ty). unwrap_or ( & field.ty); (ident. unwrap (), ty) }) . map ( | (ident, ty) | { if is_vector ( & ty) { quote! { #ident : #ty } } else { quote! { #ident : Option < #ty > } } }); quote! { #visibility struct #builder_name { #( #struct_fields ), * } } } fn build_builder_impl ( fields: & FieldsNamed, builder_name: & Ident, struct_name: & Ident, ) -> TokenStream { let checks = fields .named . iter () . filter ( | field | ! is_option ( & field.ty)) . filter ( | field | ! is_vector ( & field.ty)) . map ( | field | { let ident = field.ident. as_ref (); let err = format! ( "Required field '{}' is missing" , ident. unwrap (). to_string ()); quote! { if self . #ident . is_none () { return Err ( #err . into ()); } } }); let setters = fields.named. iter (). map ( | field | { let ident_each_name = field .attrs . first () . map ( | attr | match attr. parse_meta () { Ok ( Meta :: List (list)) => match list.nested. first () { Some ( NestedMeta :: Meta ( Meta :: NameValue (MetaNameValue { ref path, eq_token: _, lit: Lit :: Str ( ref str ), }))) => { if let Some (name) = path.segments. first () { if name.ident. to_string () != "each" { return Some ( LitOrError :: Error ( syn :: Error :: new_spanned ( list, "expected `builder(each = \" ... \" )`" , ))); } } Some ( LitOrError :: Lit ( str . value ())) } _ => None , }, _ => None , }) . flatten (); let ident = field.ident. as_ref (); let ty = unwrap_option ( & field.ty). unwrap_or ( & field.ty); match ident_each_name { Some ( LitOrError :: Lit (name)) => { let ty_each = unwrap_vector (ty). unwrap (); let ident_each = Ident :: new (name. as_str (), Span :: call_site ()); if ident. unwrap (). to_string () == name { quote! { pub fn #ident_each ( &mut self , #ident_each : #ty_each ) -> &mut Self { self . #ident . push ( #ident_each ); self } } } else { quote! { pub fn #ident ( &mut self , #ident : #ty ) -> &mut Self { self . #ident = #ident ; self } pub fn #ident_each ( &mut self , #ident_each : #ty_each ) -> &mut Self { self . #ident . push ( #ident_each ); self } } } } Some ( LitOrError :: Error (err)) => err. to_compile_error (). into (), None => { if is_vector ( & ty) { quote! { pub fn #ident ( &mut self , #ident : #ty ) -> &mut Self { self . #ident = #ident ; self } } } else { quote! { pub fn #ident ( &mut self , #ident : #ty ) -> &mut Self { self . #ident = Some ( #ident ); self } } } } } }); let struct_fields = fields.named. iter (). map ( | field | { let ident = field.ident. as_ref (); if is_option ( & field.ty) || is_vector ( & field.ty) { quote! { #ident : self . #ident . clone () } } else { quote! { #ident : self . #ident . clone (). unwrap () } } }); quote! { impl #builder_name { #( #setters ) * pub fn build ( &mut self ) -> Result < #struct_name , Box < dyn std :: error :: Error >> { #( #checks ) * Ok ( #struct_name { #( #struct_fields ), * }) } } } } fn build_struct_impl ( fields: & FieldsNamed, builder_name: & Ident, struct_name: & Ident, ) -> TokenStream { let field_defaults = fields.named. iter (). map ( | field | { let ident = field.ident. as_ref (); let ty = & field.ty; if is_vector ( & ty) { quote! { #ident : Vec :: new () } } else { quote! { #ident : None } } }); quote! { impl #struct_name { pub fn builder () -> #builder_name { #builder_name { #( #field_defaults ), * } } } } } fn is_option (ty: & Type) -> bool { match get_last_path_segment (ty) { Some (seg) => seg.ident == "Option" , _ => false , } } fn is_vector (ty: & Type) -> bool { match get_last_path_segment (ty) { Some (seg) => seg.ident == "Vec" , _ => false , } } fn unwrap_option (ty: & Type) -> Option < & Type > { if ! is_option (ty) { return None ; } unwrap_generic_type (ty) } fn unwrap_vector (ty: & Type) -> Option < & Type > { if ! is_vector (ty) { return None ; } unwrap_generic_type (ty) } fn unwrap_generic_type (ty: & Type) -> Option < & Type > { match get_last_path_segment (ty) { Some (seg) => match seg.arguments { PathArguments :: AngleBracketed ( ref args) => { args.args. first (). and_then ( | arg | match arg { & GenericArgument :: Type ( ref ty) => Some (ty), _ => None , }) } _ => None , }, None => None , } } fn get_last_path_segment (ty: & Type) -> Option < & PathSegment > { match ty { Type :: Path (path) => path.path.segments. last (), _ => None , } } 09-redefined-prelude-types ç®æš std::prelude ã§ã€ã³ããŒããããåïŒ Option ã Vector ïŒãªã©ããŠãŒã¶ãŒã«ãã£ãŠåå®çŸ©ãããŠãæ£ãã䜿ããããã«ãã å®è£
æ¹é Option ã Vec ãªã©ãåå空éãæå®ããŠäœ¿ãããã«ããã°ããã§ãããã¹ãã³ãŒãã§ãã§ãã¯ãããŠããã®ã¯ä»¥äžã® 5 ã€ãªã®ã§ãä»åã¯ãããã®é©åãªåå空éãæå®ããããã«å€æŽããŸãã 倿Žå 倿ŽåŸ Option std::option::Option Some std::option::Option::Some None std::option::Option::None Result std::result::Result Box std::boxed::Box åå®çŸ©ã®åœ±é¿ãåããã®ã¯ãã¯ããåŒã³åºãããŠå±éãããéšåã®ã¿ãªã®ã§ãçŽãã®ã¯ quote ãã¯ãã®äžã«ããéšåã ãã§ååã§ãã å®è£
äžè¿°ã® 5 ã€ã®åå空éãæ£ããæå®ããã ããªã®ã§ãä»åã¯æçµçãªçµæã®ã¿ãèšèŒããŸããæçµçãªå®è£
ã¯ä»¥äžã®ããã«ãªããŸãã use proc_macro2 :: {Span, TokenStream}; use quote :: {format_ident, quote}; use syn :: { parse_macro_input, Data, DeriveInput, Fields, FieldsNamed, GenericArgument, Ident, Lit, Meta, MetaNameValue, NestedMeta, PathArguments, PathSegment, Type, Visibility, }; enum LitOrError { Lit ( String ), Error ( syn :: Error), } #[proc_macro_derive(Builder, attributes(builder))] pub fn derive (input: proc_macro :: TokenStream) -> proc_macro :: TokenStream { let input = parse_macro_input! (input as DeriveInput); let ident = input.ident; let vis = input.vis; let builder_name = format_ident! ( "{}Builder" , ident); let fields = match input.data { Data :: Struct (data) => match data.fields { Fields :: Named (fields) => fields, _ => { return syn :: Error :: new (ident. span (), "expects named fields" ) . to_compile_error () . into () } }, _ => { return syn :: Error :: new (ident. span (), "expects struct" ) . to_compile_error () . into () } }; let builder_struct = build_builder_struct ( & fields, & builder_name, & vis); let builder_impl = build_builder_impl ( & fields, & builder_name, & ident); let struct_impl = build_struct_impl ( & fields, & builder_name, & ident); let expand = quote! { #builder_struct #builder_impl #struct_impl }; proc_macro :: TokenStream :: from (expand) } fn build_builder_struct ( fields: & FieldsNamed, builder_name: & Ident, visibility: & Visibility, ) -> TokenStream { let struct_fields = fields .named . iter () . map ( | field | { let ident = field.ident. as_ref (); let ty = unwrap_option ( & field.ty). unwrap_or ( & field.ty); (ident. unwrap (), ty) }) . map ( | (ident, ty) | { if is_vector ( & ty) { quote! { #ident : #ty } } else { quote! { #ident : std :: option :: Option < #ty > } } }); quote! { #visibility struct #builder_name { #( #struct_fields ), * } } } fn build_builder_impl ( fields: & FieldsNamed, builder_name: & Ident, struct_name: & Ident, ) -> TokenStream { let checks = fields .named . iter () . filter ( | field | ! is_option ( & field.ty)) . filter ( | field | ! is_vector ( & field.ty)) . map ( | field | { let ident = field.ident. as_ref (); let err = format! ( "Required field '{}' is missing" , ident. unwrap (). to_string ()); quote! { if self . #ident . is_none () { return Err ( #err . into ()); } } }); let setters = fields.named. iter (). map ( | field | { let ident_each_name = field .attrs . first () . map ( | attr | match attr. parse_meta () { Ok ( Meta :: List (list)) => match list.nested. first () { Some ( NestedMeta :: Meta ( Meta :: NameValue (MetaNameValue { ref path, eq_token: _, lit: Lit :: Str ( ref str ), }))) => { if let Some (name) = path.segments. first () { if name.ident. to_string () != "each" { return Some ( LitOrError :: Error ( syn :: Error :: new_spanned ( list, "expected `builder(each = \" ... \" )`" , ))); } } Some ( LitOrError :: Lit ( str . value ())) } _ => None , }, _ => None , }) . flatten (); let ident = field.ident. as_ref (); let ty = unwrap_option ( & field.ty). unwrap_or ( & field.ty); match ident_each_name { Some ( LitOrError :: Lit (name)) => { let ty_each = unwrap_vector (ty). unwrap (); let ident_each = Ident :: new (name. as_str (), Span :: call_site ()); if ident. unwrap (). to_string () == name { quote! { pub fn #ident_each ( &mut self , #ident_each : #ty_each ) -> &mut Self { self . #ident . push ( #ident_each ); self } } } else { quote! { pub fn #ident ( &mut self , #ident : #ty ) -> &mut Self { self . #ident = #ident ; self } pub fn #ident_each ( &mut self , #ident_each : #ty_each ) -> &mut Self { self . #ident . push ( #ident_each ); self } } } } Some ( LitOrError :: Error (err)) => err. to_compile_error (). into (), None => { if is_vector ( & ty) { quote! { pub fn #ident ( &mut self , #ident : #ty ) -> &mut Self { self . #ident = #ident ; self } } } else { quote! { pub fn #ident ( &mut self , #ident : #ty ) -> &mut Self { self . #ident = std :: option :: Option :: Some ( #ident ); self } } } } } }); let struct_fields = fields.named. iter (). map ( | field | { let ident = field.ident. as_ref (); if is_option ( & field.ty) || is_vector ( & field.ty) { quote! { #ident : self . #ident . clone () } } else { quote! { #ident : self . #ident . clone (). unwrap () } } }); quote! { impl #builder_name { #( #setters ) * pub fn build ( &mut self ) -> std :: result :: Result < #struct_name , std :: boxed :: Box < dyn std :: error :: Error >> { #( #checks ) * Ok ( #struct_name { #( #struct_fields ), * }) } } } } fn build_struct_impl ( fields: & FieldsNamed, builder_name: & Ident, struct_name: & Ident, ) -> TokenStream { let field_defaults = fields.named. iter (). map ( | field | { let ident = field.ident. as_ref (); let ty = & field.ty; if is_vector ( & ty) { quote! { #ident : std :: vec :: Vec :: new () } } else { quote! { #ident : std :: option :: Option :: None } } }); quote! { impl #struct_name { pub fn builder () -> #builder_name { #builder_name { #( #field_defaults ), * } } } } } fn is_option (ty: & Type) -> bool { match get_last_path_segment (ty) { Some (seg) => seg.ident == "Option" , _ => false , } } fn is_vector (ty: & Type) -> bool { match get_last_path_segment (ty) { Some (seg) => seg.ident == "Vec" , _ => false , } } fn unwrap_option (ty: & Type) -> Option < & Type > { if ! is_option (ty) { return None ; } unwrap_generic_type (ty) } fn unwrap_vector (ty: & Type) -> Option < & Type > { if ! is_vector (ty) { return None ; } unwrap_generic_type (ty) } fn unwrap_generic_type (ty: & Type) -> Option < & Type > { match get_last_path_segment (ty) { Some (seg) => match seg.arguments { PathArguments :: AngleBracketed ( ref args) => { args.args. first (). and_then ( | arg | match arg { & GenericArgument :: Type ( ref ty) => Some (ty), _ => None , }) } _ => None , }, None => None , } } fn get_last_path_segment (ty: & Type) -> Option < & PathSegment > { match ty { Type :: Path (path) => path.path.segments. last (), _ => None , } } ãŸãšã builder ãã¯ãã顿ã«ããŠåç·šãšåŸç·šã«åããŠæç¶ãçãã¯ãã®å®è£
æ¹æ³ã説æããŠããŸããã ä»åå®è£
ãããã¯ãã¯ãã£ãŒã«ãã®åã Option<Vec<_>> ã§ããã±ãŒã¹ãã Vec åã®ãã£ãŒã«ã以å€ã« each ãä»äžããå Žåãªã©ãèæ
®ããŠããããå®è£
ããæ©èœã¯ååã§ã¯ãããŸããããã¹ããååãªã±ãŒã¹ãç¶²çŸ
ããŠãããšã¯èšããŸããã ãããä»åã®èšäºã§æç¶ãçãã¯ããã©ã®ããã«ããŠäœããã¯äžéãçè§£ã§ããä»åŸããæãã©ãããæãã€ããã°ãããã ãããæèŠãã€ãããããšæããŸãããã®èšäºãä»åŸã¿ãªããããã¯ããå®è£
ãããšãã®å©ãã«ãªãã°å¹žãã§ãã åèæç® syn - Docs.rs