1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213 |
- //! 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 {
- Ok(FieldInfo {
- ordinal,
- name: &name,
- generic_ident: syn::Ident::new(
- &format!("__{}", strip_raw_ident_prefix(name.to_string())),
- proc_macro2::Span::call_site(),
- ),
- ty: &field.ty,
- builder_attr: field_defaults.with(&field.attrs)?,
- })
- } 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, Default, Clone)]
- pub struct SetterSettings {
- pub doc: Option<syn::Expr>,
- pub skip: bool,
- pub auto_into: bool,
- pub strip_option: bool,
- }
- 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("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) => {
- 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(),
- "#[builder(skip)] must be accompanied by default or default_code",
- ));
- }
- Ok(self)
- }
- pub 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() {
- "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),
- )),
- }
- }
- 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;
- 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());
- }
- 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(),
- }
- }
- }
- unsafe impl #impl_generics dioxus::prelude::Properties for #name #ty_generics{
- type Builder = #builder_name #generics_with_empty;
- const CAN_BE_MEMOIZED: bool = true;
- fn builder() -> Self::Builder {
- #name::builder()
- }
- }
- })
- }
- // 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 ),*
- }
- }
- }
- )
- }
- pub fn build_props_impl(&self) -> TokenStream {
- // SomeProps: #name
- // #builder_name
- // #generics_with_empty
- quote! {}
- }
- }
- #[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 (<...>=<...>)")),
- }
- }
- }
- }
|