From 391bbf78414b6511d3647ca884db4d67ec20c665 Mon Sep 17 00:00:00 2001 From: Markus Mayer Date: Sat, 15 Jun 2024 14:18:27 +0200 Subject: [PATCH] Apply field doc-comments to builder method Signed-off-by: Markus Mayer --- examples/example.rs | 9 +++++++-- tests/no_std.rs | 3 +++ typed-builder-macro/src/field_info.rs | 19 +++++++++++++++---- typed-builder-macro/src/struct_info.rs | 23 +++++++++++++++++++---- 4 files changed, 44 insertions(+), 10 deletions(-) diff --git a/examples/example.rs b/examples/example.rs index 3c7b5eb3..121ceb3c 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -2,12 +2,17 @@ use typed_builder::TypedBuilder; #[derive(Debug, PartialEq, TypedBuilder)] struct Foo { - // Mandatory Field: + /// `x` value. + /// + /// This field is mandatory. x: i32, // #[builder(default)] without parameter - use the type's default // #[builder(setter(strip_option))] - wrap the setter argument with `Some(...)` - #[builder(default, setter(strip_option))] + #[builder( + default, + setter(strip_option, doc = "Set `y`. If you don't specify a value it'll default to no value.",) + )] y: Option, // Or you can set the default diff --git a/tests/no_std.rs b/tests/no_std.rs index 9f8f3ae6..e56d79f5 100644 --- a/tests/no_std.rs +++ b/tests/no_std.rs @@ -52,10 +52,13 @@ fn test_into() { fn test_default() { #[derive(PartialEq, TypedBuilder)] struct Foo { + /// x value. #[builder(default, setter(strip_option))] x: Option, #[builder(default = 10)] + /// y value. y: i32, + /// z value. #[builder(default = [20, 30, 40])] z: [i32; 3], } diff --git a/typed-builder-macro/src/field_info.rs b/typed-builder-macro/src/field_info.rs index 838a8704..82ddad3c 100644 --- a/typed-builder-macro/src/field_info.rs +++ b/typed-builder-macro/src/field_info.rs @@ -115,6 +115,7 @@ pub struct FieldBuilderAttr<'a> { pub default: Option, pub via_mutators: Option, pub deprecated: Option<&'a syn::Attribute>, + pub doc_comments: Vec<&'a syn::Expr>, pub setter: SetterSettings, /// Functions that are able to mutate fields in the builder that are already set pub mutators: Vec, @@ -153,10 +154,20 @@ impl<'a> FieldBuilderAttr<'a> { list } - syn::Meta::Path(path) | syn::Meta::NameValue(syn::MetaNameValue { path, .. }) => { - if path_to_single_string(path).as_deref() == Some("deprecated") { - self.deprecated = Some(attr); - }; + syn::Meta::NameValue(syn::MetaNameValue { path, value, .. }) => { + match path_to_single_string(path).as_deref() { + Some("deprecated") => self.deprecated = Some(attr), + Some("doc") => self.doc_comments.push(value), + _ => continue, + } + + continue; + } + syn::Meta::Path(path) => { + match path_to_single_string(path).as_deref() { + Some("deprecated") => self.deprecated = Some(attr), + _ => continue, + } continue; } diff --git a/typed-builder-macro/src/struct_info.rs b/typed-builder-macro/src/struct_info.rs index 9753a375..14fd22ee 100644 --- a/typed-builder-macro/src/struct_info.rs +++ b/typed-builder-macro/src/struct_info.rs @@ -251,11 +251,25 @@ impl<'a> StructInfo<'a> { target_generics.push(syn::GenericArgument::Type(target_generics_tuple.into())); ty_generics.push(syn::GenericArgument::Type(ty_generics_tuple.into())); let (impl_generics, _, where_clause) = generics.split_for_impl(); - let doc = field.builder_attr.setter.doc.as_ref().map(|doc| quote!(#[doc = #doc])); + let doc = if let Some(doc) = field.builder_attr.setter.doc.as_ref() { + Some(quote!(#[doc = #doc])) + } else if !field.builder_attr.doc_comments.is_empty() { + Some( + field + .builder_attr + .doc_comments + .iter() + .map(|&line| quote!(#[doc = #line])) + .collect(), + ) + } else { + None + }; + let deprecated = &field.builder_attr.deprecated; // 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. + // nesting is different, so we have to do this little dance. let arg_type = if field.builder_attr.setter.strip_option.is_some() && field.builder_attr.setter.transform.is_none() { field .type_from_inside_option() @@ -320,6 +334,7 @@ impl<'a> StructInfo<'a> { #[deprecated( note = #repeated_fields_error_message )] + #doc pub fn #method_name (self, _: #repeated_fields_error_type_name) -> #builder_name <#target_generics> { self } @@ -354,8 +369,8 @@ impl<'a> StructInfo<'a> { let mut generics = self.generics.clone(); for f in self.included_fields() { if f.builder_attr.default.is_some() || f.builder_attr.via_mutators.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. + // `f` is not mandatory - it does not have its own fake `build` method, so `field` will need + // to warn about missing `field` regardless of whether `f` is set. assert!( f.ordinal != field.ordinal, "`required_field_impl` called for optional field {}",