123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251 |
- //! This code mostly comes from idanarye/rust-typed-builder
- //!
- //! However, it has been adopted to fit the Dioxus Props builder pattern.
- //!
- //! For dioxus, we make a few changes:
- //! - [ ] automatically implement Into<Option> on the setters (IE the strip setter option)
- //! - [ ] automatically implement a default of none for optional fields (those explicitly wrapped with Option<T>)
- use proc_macro2::TokenStream;
- use syn::parse::Error;
- use syn::spanned::Spanned;
- use quote::quote;
- pub fn impl_my_derive(ast: &syn::DeriveInput) -> Result<TokenStream, Error> {
- let data = match &ast.data {
- syn::Data::Struct(data) => match &data.fields {
- syn::Fields::Named(fields) => {
- let struct_info = struct_info::StructInfo::new(ast, fields.named.iter())?;
- let builder_creation = struct_info.builder_creation_impl()?;
- let conversion_helper = struct_info.conversion_helper_impl()?;
- let fields = struct_info
- .included_fields()
- .map(|f| struct_info.field_impl(f))
- .collect::<Result<Vec<_>, _>>()?;
- let fields = quote!(#(#fields)*).into_iter();
- let required_fields = struct_info
- .included_fields()
- .filter(|f| f.builder_attr.default.is_none())
- .map(|f| struct_info.required_field_impl(f))
- .collect::<Result<Vec<_>, _>>()?;
- let build_method = struct_info.build_method_impl();
- quote! {
- #builder_creation
- #conversion_helper
- #( #fields )*
- #( #required_fields )*
- #build_method
- }
- }
- syn::Fields::Unnamed(_) => {
- return Err(Error::new(
- ast.span(),
- "TypedBuilder is not supported for tuple structs",
- ))
- }
- syn::Fields::Unit => {
- return Err(Error::new(
- ast.span(),
- "TypedBuilder is not supported for unit structs",
- ))
- }
- },
- syn::Data::Enum(_) => {
- return Err(Error::new(
- ast.span(),
- "TypedBuilder is not supported for enums",
- ))
- }
- syn::Data::Union(_) => {
- return Err(Error::new(
- ast.span(),
- "TypedBuilder is not supported for unions",
- ))
- }
- };
- Ok(data)
- }
- mod util {
- use quote::ToTokens;
- pub fn path_to_single_string(path: &syn::Path) -> Option<String> {
- if path.leading_colon.is_some() {
- return None;
- }
- let mut it = path.segments.iter();
- let segment = it.next()?;
- if it.next().is_some() {
- // Multipart path
- return None;
- }
- if segment.arguments != syn::PathArguments::None {
- return None;
- }
- Some(segment.ident.to_string())
- }
- pub fn expr_to_single_string(expr: &syn::Expr) -> Option<String> {
- if let syn::Expr::Path(path) = &*expr {
- path_to_single_string(&path.path)
- } else {
- None
- }
- }
- pub fn ident_to_type(ident: syn::Ident) -> syn::Type {
- let mut path = syn::Path {
- leading_colon: None,
- segments: Default::default(),
- };
- path.segments.push(syn::PathSegment {
- ident,
- arguments: Default::default(),
- });
- syn::Type::Path(syn::TypePath { qself: None, path })
- }
- pub fn empty_type() -> syn::Type {
- syn::TypeTuple {
- paren_token: Default::default(),
- elems: Default::default(),
- }
- .into()
- }
- pub fn type_tuple(elems: impl Iterator<Item = syn::Type>) -> syn::TypeTuple {
- let mut result = syn::TypeTuple {
- paren_token: Default::default(),
- elems: elems.collect(),
- };
- if !result.elems.empty_or_trailing() {
- result.elems.push_punct(Default::default());
- }
- result
- }
- pub fn empty_type_tuple() -> syn::TypeTuple {
- syn::TypeTuple {
- paren_token: Default::default(),
- elems: Default::default(),
- }
- }
- pub fn make_punctuated_single<T, P: Default>(value: T) -> syn::punctuated::Punctuated<T, P> {
- let mut punctuated = syn::punctuated::Punctuated::new();
- punctuated.push(value);
- punctuated
- }
- pub fn modify_types_generics_hack<F>(
- ty_generics: &syn::TypeGenerics,
- mut mutator: F,
- ) -> syn::AngleBracketedGenericArguments
- where
- F: FnMut(&mut syn::punctuated::Punctuated<syn::GenericArgument, syn::token::Comma>),
- {
- let mut abga: syn::AngleBracketedGenericArguments =
- syn::parse(ty_generics.clone().into_token_stream().into()).unwrap_or_else(|_| {
- syn::AngleBracketedGenericArguments {
- colon2_token: None,
- lt_token: Default::default(),
- args: Default::default(),
- gt_token: Default::default(),
- }
- });
- mutator(&mut abga.args);
- abga
- }
- pub fn strip_raw_ident_prefix(mut name: String) -> String {
- if name.starts_with("r#") {
- name.replace_range(0..2, "");
- }
- name
- }
- }
- mod field_info {
- use proc_macro2::TokenStream;
- use quote::quote;
- use syn::parse::Error;
- use syn::spanned::Spanned;
- use super::util::{
- expr_to_single_string, ident_to_type, path_to_single_string, strip_raw_ident_prefix,
- };
- #[derive(Debug)]
- pub struct FieldInfo<'a> {
- pub ordinal: usize,
- pub name: &'a syn::Ident,
- pub generic_ident: syn::Ident,
- pub ty: &'a syn::Type,
- pub builder_attr: FieldBuilderAttr,
- }
- impl<'a> FieldInfo<'a> {
- pub fn new(
- ordinal: usize,
- field: &syn::Field,
- field_defaults: FieldBuilderAttr,
- ) -> Result<FieldInfo, Error> {
- if let Some(ref name) = field.ident {
- let mut builder_attr = field_defaults.with(&field.attrs)?;
- // children field is automatically defaulted to None
- if name == "children" {
- builder_attr.default =
- Some(syn::parse(quote!(Default::default()).into()).unwrap());
- }
- Ok(FieldInfo {
- ordinal,
- name,
- generic_ident: syn::Ident::new(
- &format!("__{}", strip_raw_ident_prefix(name.to_string())),
- proc_macro2::Span::call_site(),
- ),
- ty: &field.ty,
- builder_attr,
- })
- } else {
- Err(Error::new(field.span(), "Nameless field in struct"))
- }
- }
- pub fn generic_ty_param(&self) -> syn::GenericParam {
- syn::GenericParam::Type(self.generic_ident.clone().into())
- }
- pub fn type_ident(&self) -> syn::Type {
- ident_to_type(self.generic_ident.clone())
- }
- pub fn tuplized_type_ty_param(&self) -> syn::Type {
- let mut types = syn::punctuated::Punctuated::default();
- types.push(self.ty.clone());
- types.push_punct(Default::default());
- syn::TypeTuple {
- paren_token: Default::default(),
- elems: types,
- }
- .into()
- }
- pub fn type_from_inside_option(&self) -> Option<&syn::Type> {
- let path = if let syn::Type::Path(type_path) = self.ty {
- if type_path.qself.is_some() {
- return None;
- } else {
- &type_path.path
- }
- } else {
- return None;
- };
- let segment = path.segments.last()?;
- if segment.ident != "Option" {
- return None;
- }
- let generic_params =
- if let syn::PathArguments::AngleBracketed(generic_params) = &segment.arguments {
- generic_params
- } else {
- return None;
- };
- if let syn::GenericArgument::Type(ty) = generic_params.args.first()? {
- Some(ty)
- } else {
- None
- }
- }
- }
- #[derive(Debug, Default, Clone)]
- pub struct FieldBuilderAttr {
- pub default: Option<syn::Expr>,
- pub setter: SetterSettings,
- }
- #[derive(Debug, Clone)]
- pub struct SetterSettings {
- pub doc: Option<syn::Expr>,
- pub skip: bool,
- pub auto_into: bool,
- pub strip_option: bool,
- }
- impl Default for SetterSettings {
- fn default() -> Self {
- Self {
- doc: Default::default(),
- skip: false,
- auto_into: false,
- strip_option: false,
- }
- }
- }
- impl FieldBuilderAttr {
- pub fn with(mut self, attrs: &[syn::Attribute]) -> Result<Self, Error> {
- let mut skip_tokens = None;
- for attr in attrs {
- if path_to_single_string(&attr.path).as_deref() != Some("props") {
- continue;
- }
- if attr.tokens.is_empty() {
- continue;
- }
- let as_expr: syn::Expr = syn::parse2(attr.tokens.clone())?;
- match as_expr {
- syn::Expr::Paren(body) => {
- self.apply_meta(*body.expr)?;
- }
- syn::Expr::Tuple(body) => {
- for expr in body.elems.into_iter() {
- self.apply_meta(expr)?;
- }
- }
- _ => {
- return Err(Error::new_spanned(attr.tokens.clone(), "Expected (<...>)"));
- }
- }
- // Stash its span for later (we don’t yet know if it’ll be an error)
- if self.setter.skip && skip_tokens.is_none() {
- skip_tokens = Some(attr.tokens.clone());
- }
- }
- if self.setter.skip && self.default.is_none() {
- return Err(Error::new_spanned(
- skip_tokens.unwrap(),
- "#[props(skip)] must be accompanied by default or default_code",
- ));
- }
- Ok(self)
- }
- pub fn apply_meta(&mut self, expr: syn::Expr) -> Result<(), Error> {
- match expr {
- // #[props(default = "...")]
- syn::Expr::Assign(assign) => {
- let name = expr_to_single_string(&assign.left)
- .ok_or_else(|| Error::new_spanned(&assign.left, "Expected identifier"))?;
- match name.as_str() {
- "default" => {
- self.default = Some(*assign.right);
- Ok(())
- }
- "default_code" => {
- if let syn::Expr::Lit(syn::ExprLit {
- lit: syn::Lit::Str(code),
- ..
- }) = *assign.right
- {
- use std::str::FromStr;
- let tokenized_code = TokenStream::from_str(&code.value())?;
- self.default = Some(
- syn::parse(tokenized_code.into())
- .map_err(|e| Error::new_spanned(code, format!("{}", e)))?,
- );
- } else {
- return Err(Error::new_spanned(assign.right, "Expected string"));
- }
- Ok(())
- }
- _ => Err(Error::new_spanned(
- &assign,
- format!("Unknown parameter {:?}", name),
- )),
- }
- }
- // uh not sure
- syn::Expr::Path(path) => {
- let name = path_to_single_string(&path.path)
- .ok_or_else(|| Error::new_spanned(&path, "Expected identifier"))?;
- match name.as_str() {
- "default" => {
- self.default =
- Some(syn::parse(quote!(Default::default()).into()).unwrap());
- Ok(())
- }
- _ => Err(Error::new_spanned(
- &path,
- format!("Unknown parameter {:?}", name),
- )),
- }
- }
- //
- syn::Expr::Call(call) => {
- let subsetting_name = if let syn::Expr::Path(path) = &*call.func {
- path_to_single_string(&path.path)
- } else {
- None
- }
- .ok_or_else(|| {
- let call_func = &call.func;
- let call_func = quote!(#call_func);
- Error::new_spanned(
- &call.func,
- format!("Illegal builder setting group {}", call_func),
- )
- })?;
- match subsetting_name.as_ref() {
- "setter" => {
- for arg in call.args {
- self.setter.apply_meta(arg)?;
- }
- Ok(())
- }
- _ => Err(Error::new_spanned(
- &call.func,
- format!("Illegal builder setting group name {}", subsetting_name),
- )),
- }
- }
- syn::Expr::Unary(syn::ExprUnary {
- op: syn::UnOp::Not(_),
- expr,
- ..
- }) => {
- if let syn::Expr::Path(path) = *expr {
- let name = path_to_single_string(&path.path)
- .ok_or_else(|| Error::new_spanned(&path, "Expected identifier"))?;
- match name.as_str() {
- "default" => {
- self.default = None;
- Ok(())
- }
- _ => Err(Error::new_spanned(path, "Unknown setting".to_owned())),
- }
- } else {
- Err(Error::new_spanned(
- expr,
- "Expected simple identifier".to_owned(),
- ))
- }
- }
- _ => Err(Error::new_spanned(expr, "Expected (<...>=<...>)")),
- }
- }
- }
- impl SetterSettings {
- fn apply_meta(&mut self, expr: syn::Expr) -> Result<(), Error> {
- match expr {
- syn::Expr::Assign(assign) => {
- let name = expr_to_single_string(&assign.left)
- .ok_or_else(|| Error::new_spanned(&assign.left, "Expected identifier"))?;
- match name.as_str() {
- "doc" => {
- self.doc = Some(*assign.right);
- Ok(())
- }
- _ => Err(Error::new_spanned(
- &assign,
- format!("Unknown parameter {:?}", name),
- )),
- }
- }
- syn::Expr::Path(path) => {
- let name = path_to_single_string(&path.path)
- .ok_or_else(|| Error::new_spanned(&path, "Expected identifier"))?;
- macro_rules! handle_fields {
- ( $( $flag:expr, $field:ident, $already:expr; )* ) => {
- match name.as_str() {
- $(
- $flag => {
- if self.$field {
- Err(Error::new(path.span(), concat!("Illegal setting - field is already ", $already)))
- } else {
- self.$field = true;
- Ok(())
- }
- }
- )*
- _ => Err(Error::new_spanned(
- &path,
- format!("Unknown setter parameter {:?}", name),
- ))
- }
- }
- }
- handle_fields!(
- "skip", skip, "skipped";
- "into", auto_into, "calling into() on the argument";
- "strip_option", strip_option, "putting the argument in Some(...)";
- )
- }
- syn::Expr::Unary(syn::ExprUnary {
- op: syn::UnOp::Not(_),
- expr,
- ..
- }) => {
- if let syn::Expr::Path(path) = *expr {
- let name = path_to_single_string(&path.path)
- .ok_or_else(|| Error::new_spanned(&path, "Expected identifier"))?;
- match name.as_str() {
- "doc" => {
- self.doc = None;
- Ok(())
- }
- "skip" => {
- self.skip = false;
- Ok(())
- }
- "auto_into" => {
- self.auto_into = false;
- Ok(())
- }
- "strip_option" => {
- self.strip_option = false;
- Ok(())
- }
- _ => Err(Error::new_spanned(path, "Unknown setting".to_owned())),
- }
- } else {
- Err(Error::new_spanned(
- expr,
- "Expected simple identifier".to_owned(),
- ))
- }
- }
- _ => Err(Error::new_spanned(expr, "Expected (<...>=<...>)")),
- }
- }
- }
- }
- mod struct_info {
- use proc_macro2::TokenStream;
- use quote::quote;
- use syn::parse::Error;
- use super::field_info::{FieldBuilderAttr, FieldInfo};
- use super::util::{
- empty_type, empty_type_tuple, expr_to_single_string, make_punctuated_single,
- modify_types_generics_hack, path_to_single_string, strip_raw_ident_prefix, type_tuple,
- };
- #[derive(Debug)]
- pub struct StructInfo<'a> {
- pub vis: &'a syn::Visibility,
- pub name: &'a syn::Ident,
- pub generics: &'a syn::Generics,
- pub fields: Vec<FieldInfo<'a>>,
- pub builder_attr: TypeBuilderAttr,
- pub builder_name: syn::Ident,
- pub conversion_helper_trait_name: syn::Ident,
- pub core: syn::Ident,
- }
- impl<'a> StructInfo<'a> {
- pub fn included_fields(&self) -> impl Iterator<Item = &FieldInfo<'a>> {
- self.fields.iter().filter(|f| !f.builder_attr.setter.skip)
- }
- pub fn new(
- ast: &'a syn::DeriveInput,
- fields: impl Iterator<Item = &'a syn::Field>,
- ) -> Result<StructInfo<'a>, Error> {
- let builder_attr = TypeBuilderAttr::new(&ast.attrs)?;
- let builder_name = strip_raw_ident_prefix(format!("{}Builder", ast.ident));
- Ok(StructInfo {
- vis: &ast.vis,
- name: &ast.ident,
- generics: &ast.generics,
- fields: fields
- .enumerate()
- .map(|(i, f)| FieldInfo::new(i, f, builder_attr.field_defaults.clone()))
- .collect::<Result<_, _>>()?,
- builder_attr,
- builder_name: syn::Ident::new(&builder_name, proc_macro2::Span::call_site()),
- conversion_helper_trait_name: syn::Ident::new(
- &format!("{}_Optional", builder_name),
- proc_macro2::Span::call_site(),
- ),
- core: syn::Ident::new(
- &format!("{}_core", builder_name),
- proc_macro2::Span::call_site(),
- ),
- })
- }
- fn modify_generics<F: FnMut(&mut syn::Generics)>(&self, mut mutator: F) -> syn::Generics {
- let mut generics = self.generics.clone();
- mutator(&mut generics);
- generics
- }
- pub fn builder_creation_impl(&self) -> Result<TokenStream, Error> {
- let StructInfo {
- ref vis,
- ref name,
- ref builder_name,
- ..
- } = *self;
- // we're generating stuff that goes into unsafe code here
- // we use the heuristic: are there *any* generic parameters?
- // If so, then they might have non-static lifetimes and we can't compare two generic things that *might borrow*
- // Therefore, we will generate code that shortcircuits the "comparison" in memoization
- let are_there_generics = !self.generics.params.is_empty();
- let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
- let all_fields_param = syn::GenericParam::Type(
- syn::Ident::new("TypedBuilderFields", proc_macro2::Span::call_site()).into(),
- );
- let b_generics = self.modify_generics(|g| {
- g.params.insert(0, all_fields_param.clone());
- });
- let empties_tuple = type_tuple(self.included_fields().map(|_| empty_type()));
- let generics_with_empty = modify_types_generics_hack(&ty_generics, |args| {
- args.insert(0, syn::GenericArgument::Type(empties_tuple.clone().into()));
- });
- let phantom_generics = self.generics.params.iter().map(|param| match param {
- syn::GenericParam::Lifetime(lifetime) => {
- let lifetime = &lifetime.lifetime;
- quote!(core::marker::PhantomData<&#lifetime ()>)
- }
- syn::GenericParam::Type(ty) => {
- let ty = &ty.ident;
- quote!(core::marker::PhantomData<#ty>)
- }
- syn::GenericParam::Const(_cnst) => {
- quote!()
- }
- });
- let builder_method_doc = match self.builder_attr.builder_method_doc {
- Some(ref doc) => quote!(#doc),
- None => {
- let doc = format!(
- "
- Create a builder for building `{name}`.
- On the builder, call {setters} to set the values of the fields.
- Finally, call `.build()` to create the instance of `{name}`.
- ",
- name = self.name,
- setters = {
- let mut result = String::new();
- let mut is_first = true;
- for field in self.included_fields() {
- use std::fmt::Write;
- if is_first {
- is_first = false;
- } else {
- write!(&mut result, ", ").unwrap();
- }
- write!(&mut result, "`.{}(...)`", field.name).unwrap();
- if field.builder_attr.default.is_some() {
- write!(&mut result, "(optional)").unwrap();
- }
- }
- result
- }
- );
- quote!(#doc)
- }
- };
- let builder_type_doc = if self.builder_attr.doc {
- match self.builder_attr.builder_type_doc {
- Some(ref doc) => quote!(#[doc = #doc]),
- None => {
- let doc = format!(
- "Builder for [`{name}`] instances.\n\nSee [`{name}::builder()`] for more info.",
- name = name
- );
- quote!(#[doc = #doc])
- }
- }
- } else {
- quote!(#[doc(hidden)])
- };
- let (b_generics_impl, b_generics_ty, b_generics_where_extras_predicates) =
- b_generics.split_for_impl();
- let mut b_generics_where: syn::WhereClause = syn::parse2(quote! {
- where TypedBuilderFields: Clone
- })?;
- if let Some(predicates) = b_generics_where_extras_predicates {
- b_generics_where
- .predicates
- .extend(predicates.predicates.clone());
- }
- let can_memoize = match are_there_generics {
- true => quote! { false },
- false => quote! { self == other },
- };
- let is_static = match are_there_generics {
- true => quote! { false },
- false => quote! { true },
- };
- Ok(quote! {
- impl #impl_generics #name #ty_generics #where_clause {
- #[doc = #builder_method_doc]
- #[allow(dead_code)]
- #vis fn builder() -> #builder_name #generics_with_empty {
- #builder_name {
- fields: #empties_tuple,
- _phantom: core::default::Default::default(),
- }
- }
- }
- #[must_use]
- #builder_type_doc
- #[allow(dead_code, non_camel_case_types, non_snake_case)]
- #vis struct #builder_name #b_generics {
- fields: #all_fields_param,
- _phantom: (#( #phantom_generics ),*),
- }
- impl #b_generics_impl Clone for #builder_name #b_generics_ty #b_generics_where {
- fn clone(&self) -> Self {
- Self {
- fields: self.fields.clone(),
- _phantom: Default::default(),
- }
- }
- }
- impl #impl_generics dioxus::prelude::Properties for #name #ty_generics{
- type Builder = #builder_name #generics_with_empty;
- const IS_STATIC: bool = #is_static;
- fn builder() -> Self::Builder {
- #name::builder()
- }
- unsafe fn memoize(&self, other: &Self) -> bool {
- #can_memoize
- }
- }
- })
- }
- // TODO: once the proc-macro crate limitation is lifted, make this an util trait of this
- // crate.
- pub fn conversion_helper_impl(&self) -> Result<TokenStream, Error> {
- let trait_name = &self.conversion_helper_trait_name;
- Ok(quote! {
- #[doc(hidden)]
- #[allow(dead_code, non_camel_case_types, non_snake_case)]
- pub trait #trait_name<T> {
- fn into_value<F: FnOnce() -> T>(self, default: F) -> T;
- }
- impl<T> #trait_name<T> for () {
- fn into_value<F: FnOnce() -> T>(self, default: F) -> T {
- default()
- }
- }
- impl<T> #trait_name<T> for (T,) {
- fn into_value<F: FnOnce() -> T>(self, _: F) -> T {
- self.0
- }
- }
- })
- }
- pub fn field_impl(&self, field: &FieldInfo) -> Result<TokenStream, Error> {
- let StructInfo {
- ref builder_name, ..
- } = *self;
- let descructuring = self.included_fields().map(|f| {
- if f.ordinal == field.ordinal {
- quote!(_)
- } else {
- let name = f.name;
- quote!(#name)
- }
- });
- let reconstructing = self.included_fields().map(|f| f.name);
- let &FieldInfo {
- name: ref field_name,
- ty: ref field_type,
- ..
- } = field;
- let mut ty_generics: Vec<syn::GenericArgument> = self
- .generics
- .params
- .iter()
- .map(|generic_param| match generic_param {
- syn::GenericParam::Type(type_param) => {
- let ident = type_param.ident.clone();
- syn::parse(quote!(#ident).into()).unwrap()
- }
- syn::GenericParam::Lifetime(lifetime_def) => {
- syn::GenericArgument::Lifetime(lifetime_def.lifetime.clone())
- }
- syn::GenericParam::Const(const_param) => {
- let ident = const_param.ident.clone();
- syn::parse(quote!(#ident).into()).unwrap()
- }
- })
- .collect();
- let mut target_generics_tuple = empty_type_tuple();
- let mut ty_generics_tuple = empty_type_tuple();
- let generics = self.modify_generics(|g| {
- let index_after_lifetime_in_generics = g
- .params
- .iter()
- .filter(|arg| matches!(arg, syn::GenericParam::Lifetime(_)))
- .count();
- for f in self.included_fields() {
- if f.ordinal == field.ordinal {
- ty_generics_tuple.elems.push_value(empty_type());
- target_generics_tuple
- .elems
- .push_value(f.tuplized_type_ty_param());
- } else {
- g.params
- .insert(index_after_lifetime_in_generics, f.generic_ty_param());
- let generic_argument: syn::Type = f.type_ident();
- ty_generics_tuple.elems.push_value(generic_argument.clone());
- target_generics_tuple.elems.push_value(generic_argument);
- }
- ty_generics_tuple.elems.push_punct(Default::default());
- target_generics_tuple.elems.push_punct(Default::default());
- }
- });
- let mut target_generics = ty_generics.clone();
- let index_after_lifetime_in_generics = target_generics
- .iter()
- .filter(|arg| matches!(arg, syn::GenericArgument::Lifetime(_)))
- .count();
- target_generics.insert(
- index_after_lifetime_in_generics,
- syn::GenericArgument::Type(target_generics_tuple.into()),
- );
- ty_generics.insert(
- index_after_lifetime_in_generics,
- syn::GenericArgument::Type(ty_generics_tuple.into()),
- );
- let (impl_generics, _, where_clause) = generics.split_for_impl();
- let doc = match field.builder_attr.setter.doc {
- Some(ref doc) => quote!(#[doc = #doc]),
- None => quote!(),
- };
- // NOTE: both auto_into and strip_option affect `arg_type` and `arg_expr`, but the order of
- // nesting is different so we have to do this little dance.
- let arg_type = if field.builder_attr.setter.strip_option {
- let internal_type = field.type_from_inside_option().ok_or_else(|| {
- Error::new_spanned(
- &field_type,
- "can't `strip_option` - field is not `Option<...>`",
- )
- })?;
- internal_type
- } else {
- field_type
- };
- let (arg_type, arg_expr) = if field.builder_attr.setter.auto_into {
- (
- quote!(impl core::convert::Into<#arg_type>),
- quote!(#field_name.into()),
- )
- } else {
- (quote!(#arg_type), quote!(#field_name))
- };
- let arg_expr = if field.builder_attr.setter.strip_option {
- quote!(Some(#arg_expr))
- } else {
- arg_expr
- };
- let repeated_fields_error_type_name = syn::Ident::new(
- &format!(
- "{}_Error_Repeated_field_{}",
- builder_name,
- strip_raw_ident_prefix(field_name.to_string())
- ),
- proc_macro2::Span::call_site(),
- );
- let repeated_fields_error_message = format!("Repeated field {}", field_name);
- Ok(quote! {
- #[allow(dead_code, non_camel_case_types, missing_docs)]
- impl #impl_generics #builder_name < #( #ty_generics ),* > #where_clause {
- #doc
- pub fn #field_name (self, #field_name: #arg_type) -> #builder_name < #( #target_generics ),* > {
- let #field_name = (#arg_expr,);
- let ( #(#descructuring,)* ) = self.fields;
- #builder_name {
- fields: ( #(#reconstructing,)* ),
- _phantom: self._phantom,
- }
- }
- }
- #[doc(hidden)]
- #[allow(dead_code, non_camel_case_types, non_snake_case)]
- pub enum #repeated_fields_error_type_name {}
- #[doc(hidden)]
- #[allow(dead_code, non_camel_case_types, missing_docs)]
- impl #impl_generics #builder_name < #( #target_generics ),* > #where_clause {
- #[deprecated(
- note = #repeated_fields_error_message
- )]
- pub fn #field_name (self, _: #repeated_fields_error_type_name) -> #builder_name < #( #target_generics ),* > {
- self
- }
- }
- })
- }
- pub fn required_field_impl(&self, field: &FieldInfo) -> Result<TokenStream, Error> {
- let StructInfo {
- ref name,
- ref builder_name,
- ..
- } = self;
- let FieldInfo {
- name: ref field_name,
- ..
- } = field;
- let mut builder_generics: Vec<syn::GenericArgument> = self
- .generics
- .params
- .iter()
- .map(|generic_param| match generic_param {
- syn::GenericParam::Type(type_param) => {
- let ident = &type_param.ident;
- syn::parse(quote!(#ident).into()).unwrap()
- }
- syn::GenericParam::Lifetime(lifetime_def) => {
- syn::GenericArgument::Lifetime(lifetime_def.lifetime.clone())
- }
- syn::GenericParam::Const(const_param) => {
- let ident = &const_param.ident;
- syn::parse(quote!(#ident).into()).unwrap()
- }
- })
- .collect();
- let mut builder_generics_tuple = empty_type_tuple();
- let generics = self.modify_generics(|g| {
- let index_after_lifetime_in_generics = g
- .params
- .iter()
- .filter(|arg| matches!(arg, syn::GenericParam::Lifetime(_)))
- .count();
- for f in self.included_fields() {
- if f.builder_attr.default.is_some() {
- // `f` is not mandatory - it does not have it's own fake `build` method, so `field` will need
- // to warn about missing `field` whether or not `f` is set.
- assert!(
- f.ordinal != field.ordinal,
- "`required_field_impl` called for optional field {}",
- field.name
- );
- g.params
- .insert(index_after_lifetime_in_generics, f.generic_ty_param());
- builder_generics_tuple.elems.push_value(f.type_ident());
- } else if f.ordinal < field.ordinal {
- // Only add a `build` method that warns about missing `field` if `f` is set. If `f` is not set,
- // `f`'s `build` method will warn, since it appears earlier in the argument list.
- builder_generics_tuple
- .elems
- .push_value(f.tuplized_type_ty_param());
- } else if f.ordinal == field.ordinal {
- builder_generics_tuple.elems.push_value(empty_type());
- } else {
- // `f` appears later in the argument list after `field`, so if they are both missing we will
- // show a warning for `field` and not for `f` - which means this warning should appear whether
- // or not `f` is set.
- g.params
- .insert(index_after_lifetime_in_generics, f.generic_ty_param());
- builder_generics_tuple.elems.push_value(f.type_ident());
- }
- builder_generics_tuple.elems.push_punct(Default::default());
- }
- });
- let index_after_lifetime_in_generics = builder_generics
- .iter()
- .filter(|arg| matches!(arg, syn::GenericArgument::Lifetime(_)))
- .count();
- builder_generics.insert(
- index_after_lifetime_in_generics,
- syn::GenericArgument::Type(builder_generics_tuple.into()),
- );
- let (impl_generics, _, where_clause) = generics.split_for_impl();
- let (_, ty_generics, _) = self.generics.split_for_impl();
- let early_build_error_type_name = syn::Ident::new(
- &format!(
- "{}_Error_Missing_required_field_{}",
- builder_name,
- strip_raw_ident_prefix(field_name.to_string())
- ),
- proc_macro2::Span::call_site(),
- );
- let early_build_error_message = format!("Missing required field {}", field_name);
- Ok(quote! {
- #[doc(hidden)]
- #[allow(dead_code, non_camel_case_types, non_snake_case)]
- pub enum #early_build_error_type_name {}
- #[doc(hidden)]
- #[allow(dead_code, non_camel_case_types, missing_docs, clippy::panic)]
- impl #impl_generics #builder_name < #( #builder_generics ),* > #where_clause {
- #[deprecated(
- note = #early_build_error_message
- )]
- pub fn build(self, _: #early_build_error_type_name) -> #name #ty_generics {
- panic!();
- }
- }
- })
- }
- pub fn build_method_impl(&self) -> TokenStream {
- let StructInfo {
- ref name,
- ref builder_name,
- ..
- } = *self;
- let generics = self.modify_generics(|g| {
- let index_after_lifetime_in_generics = g
- .params
- .iter()
- .filter(|arg| matches!(arg, syn::GenericParam::Lifetime(_)))
- .count();
- for field in self.included_fields() {
- if field.builder_attr.default.is_some() {
- let trait_ref = syn::TraitBound {
- paren_token: None,
- lifetimes: None,
- modifier: syn::TraitBoundModifier::None,
- path: syn::PathSegment {
- ident: self.conversion_helper_trait_name.clone(),
- arguments: syn::PathArguments::AngleBracketed(
- syn::AngleBracketedGenericArguments {
- colon2_token: None,
- lt_token: Default::default(),
- args: make_punctuated_single(syn::GenericArgument::Type(
- field.ty.clone(),
- )),
- gt_token: Default::default(),
- },
- ),
- }
- .into(),
- };
- let mut generic_param: syn::TypeParam = field.generic_ident.clone().into();
- generic_param.bounds.push(trait_ref.into());
- g.params
- .insert(index_after_lifetime_in_generics, generic_param.into());
- }
- }
- });
- let (impl_generics, _, _) = generics.split_for_impl();
- let (_, ty_generics, where_clause) = self.generics.split_for_impl();
- let modified_ty_generics = modify_types_generics_hack(&ty_generics, |args| {
- args.insert(
- 0,
- syn::GenericArgument::Type(
- type_tuple(self.included_fields().map(|field| {
- if field.builder_attr.default.is_some() {
- field.type_ident()
- } else {
- field.tuplized_type_ty_param()
- }
- }))
- .into(),
- ),
- );
- });
- let descructuring = self.included_fields().map(|f| f.name);
- let helper_trait_name = &self.conversion_helper_trait_name;
- // The default of a field can refer to earlier-defined fields, which we handle by
- // writing out a bunch of `let` statements first, which can each refer to earlier ones.
- // This means that field ordering may actually be significant, which isn’t ideal. We could
- // relax that restriction by calculating a DAG of field default dependencies and
- // reordering based on that, but for now this much simpler thing is a reasonable approach.
- let assignments = self.fields.iter().map(|field| {
- let name = &field.name;
- if let Some(ref default) = field.builder_attr.default {
- if field.builder_attr.setter.skip {
- quote!(let #name = #default;)
- } else {
- quote!(let #name = #helper_trait_name::into_value(#name, || #default);)
- }
- } else {
- quote!(let #name = #name.0;)
- }
- });
- let field_names = self.fields.iter().map(|field| field.name);
- let doc = if self.builder_attr.doc {
- match self.builder_attr.build_method_doc {
- Some(ref doc) => quote!(#[doc = #doc]),
- None => {
- // I’d prefer “a” or “an” to “its”, but determining which is grammatically
- // correct is roughly impossible.
- let doc =
- format!("Finalise the builder and create its [`{}`] instance", name);
- quote!(#[doc = #doc])
- }
- }
- } else {
- quote!()
- };
- quote!(
- #[allow(dead_code, non_camel_case_types, missing_docs)]
- impl #impl_generics #builder_name #modified_ty_generics #where_clause {
- #doc
- pub fn build(self) -> #name #ty_generics {
- let ( #(#descructuring,)* ) = self.fields;
- #( #assignments )*
- #name {
- #( #field_names ),*
- }
- }
- }
- )
- }
- }
- #[derive(Debug, Default)]
- pub struct TypeBuilderAttr {
- /// Whether to show docs for the `TypeBuilder` type (rather than hiding them).
- pub doc: bool,
- /// Docs on the `Type::builder()` method.
- pub builder_method_doc: Option<syn::Expr>,
- /// Docs on the `TypeBuilder` type. Specifying this implies `doc`, but you can just specify
- /// `doc` instead and a default value will be filled in here.
- pub builder_type_doc: Option<syn::Expr>,
- /// Docs on the `TypeBuilder.build()` method. Specifying this implies `doc`, but you can just
- /// specify `doc` instead and a default value will be filled in here.
- pub build_method_doc: Option<syn::Expr>,
- pub field_defaults: FieldBuilderAttr,
- }
- impl TypeBuilderAttr {
- pub fn new(attrs: &[syn::Attribute]) -> Result<TypeBuilderAttr, Error> {
- let mut result = TypeBuilderAttr::default();
- for attr in attrs {
- if path_to_single_string(&attr.path).as_deref() != Some("builder") {
- continue;
- }
- if attr.tokens.is_empty() {
- continue;
- }
- let as_expr: syn::Expr = syn::parse2(attr.tokens.clone())?;
- match as_expr {
- syn::Expr::Paren(body) => {
- result.apply_meta(*body.expr)?;
- }
- syn::Expr::Tuple(body) => {
- for expr in body.elems.into_iter() {
- result.apply_meta(expr)?;
- }
- }
- _ => {
- return Err(Error::new_spanned(attr.tokens.clone(), "Expected (<...>)"));
- }
- }
- }
- Ok(result)
- }
- fn apply_meta(&mut self, expr: syn::Expr) -> Result<(), Error> {
- match expr {
- syn::Expr::Assign(assign) => {
- let name = expr_to_single_string(&assign.left)
- .ok_or_else(|| Error::new_spanned(&assign.left, "Expected identifier"))?;
- match name.as_str() {
- "builder_method_doc" => {
- self.builder_method_doc = Some(*assign.right);
- Ok(())
- }
- "builder_type_doc" => {
- self.builder_type_doc = Some(*assign.right);
- self.doc = true;
- Ok(())
- }
- "build_method_doc" => {
- self.build_method_doc = Some(*assign.right);
- self.doc = true;
- Ok(())
- }
- _ => Err(Error::new_spanned(
- &assign,
- format!("Unknown parameter {:?}", name),
- )),
- }
- }
- syn::Expr::Path(path) => {
- let name = path_to_single_string(&path.path)
- .ok_or_else(|| Error::new_spanned(&path, "Expected identifier"))?;
- match name.as_str() {
- "doc" => {
- self.doc = true;
- Ok(())
- }
- _ => Err(Error::new_spanned(
- &path,
- format!("Unknown parameter {:?}", name),
- )),
- }
- }
- syn::Expr::Call(call) => {
- let subsetting_name = if let syn::Expr::Path(path) = &*call.func {
- path_to_single_string(&path.path)
- } else {
- None
- }
- .ok_or_else(|| {
- let call_func = &call.func;
- let call_func = quote!(#call_func);
- Error::new_spanned(
- &call.func,
- format!("Illegal builder setting group {}", call_func),
- )
- })?;
- match subsetting_name.as_str() {
- "field_defaults" => {
- for arg in call.args {
- self.field_defaults.apply_meta(arg)?;
- }
- Ok(())
- }
- _ => Err(Error::new_spanned(
- &call.func,
- format!("Illegal builder setting group name {}", subsetting_name),
- )),
- }
- }
- _ => Err(Error::new_spanned(expr, "Expected (<...>=<...>)")),
- }
- }
- }
- }
|