From f179c5531192e5e0c65cd36650363968dd6db3cd Mon Sep 17 00:00:00 2001 From: Kevin Guthrie Date: Tue, 27 Aug 2024 07:47:53 -0400 Subject: [PATCH] feat(DeriveAttribute) Implement derive macro attribute for format --- tests/tests/derive.rs | 26 ++++++++++++++++++++++++++ valuable-derive/src/attr.rs | 17 ++++++++++++++++- valuable-derive/src/expand.rs | 23 ++++++++++++++++++----- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/tests/tests/derive.rs b/tests/tests/derive.rs index 996881b..30d7213 100644 --- a/tests/tests/derive.rs +++ b/tests/tests/derive.rs @@ -205,3 +205,29 @@ fn ui() { let t = trybuild::TestCases::new(); t.compile_fail("tests/ui/*.rs"); } + +#[test] +fn test_format_not_valuable() { + #[derive(Debug)] + struct NotValuable; + + #[derive(Valuable)] + struct S { + f1: (), + #[valuable(format = "{:?}")] + f2: NotValuable, + f3: (), + } + + #[derive(Valuable)] + struct T((), #[valuable(format = "{:?}")] NotValuable, ()); + + let s = Structable::definition(&S { + f1: (), + f2: NotValuable, + f3: (), + }); + assert!(matches!(s.fields(), Fields::Named(f) if f.len() == 3)); + let s = Structable::definition(&T((), NotValuable, ())); + assert!(matches!(s.fields(), Fields::Unnamed(f) if *f == 3)); +} diff --git a/valuable-derive/src/attr.rs b/valuable-derive/src/attr.rs index 45a0e0b..e41497f 100644 --- a/valuable-derive/src/attr.rs +++ b/valuable-derive/src/attr.rs @@ -42,12 +42,20 @@ static ATTRS: &[AttrDef] = &[ ], style: &[MetaStyle::Ident], }, + // #[valuable(format)] + AttrDef { + name: "format", + conflicts_with: &["skip", "rename"], + position: &[Position::NamedField, Position::UnnamedField], + style: &[MetaStyle::NameValue], + }, ]; pub(crate) struct Attrs { rename: Option<(syn::MetaNameValue, syn::LitStr)>, transparent: Option, skip: Option, + format: Option<(syn::MetaNameValue, syn::LitStr)>, } impl Attrs { @@ -65,12 +73,17 @@ impl Attrs { pub(crate) fn skip(&self) -> bool { self.skip.is_some() } + + pub(crate) fn format(&self) -> Option { + self.format.as_ref().map(|(_, format)| format).cloned() + } } pub(crate) fn parse_attrs(cx: &Context, attrs: &[syn::Attribute], pos: Position) -> Attrs { let mut rename = None; let mut transparent = None; let mut skip = None; + let mut format = None; let attrs = filter_attrs(cx, attrs, pos); for (def, meta) in &attrs { @@ -104,7 +117,8 @@ pub(crate) fn parse_attrs(cx: &Context, attrs: &[syn::Attribute], pos: Position) "transparent" => transparent = Some(meta.span()), // #[valuable(skip)] "skip" => skip = Some(meta.span()), - + // #[valuable(format = "{}")] + "format" => lit_str!(format), _ => unreachable!("{}", def.name), } } @@ -113,6 +127,7 @@ pub(crate) fn parse_attrs(cx: &Context, attrs: &[syn::Attribute], pos: Position) rename, transparent, skip, + format, } } diff --git a/valuable-derive/src/expand.rs b/valuable-derive/src/expand.rs index 25fd30c..34230c4 100644 --- a/valuable-derive/src/expand.rs +++ b/valuable-derive/src/expand.rs @@ -101,11 +101,18 @@ fn derive_struct( .iter() .enumerate() .filter(|(i, _)| !field_attrs[*i].skip()) - .map(|(_, field)| { + .map(|(i, field)| { let f = field.ident.as_ref(); - let tokens = quote! { - &self.#f + let tokens = if let Some(format_str) = field_attrs[i].format() { + quote! { + &format!(#format_str, self.#f) + } + } else { + quote! { + &self.#f + } }; + respan(tokens, &field.ty) }); visit_fields = quote! { @@ -125,8 +132,14 @@ fn derive_struct( .filter(|(i, _)| !field_attrs[*i].skip()) .map(|(i, field)| { let index = syn::Index::from(i); - let tokens = quote! { - &self.#index + let tokens = if let Some(format_str) = field_attrs[i].format() { + quote! { + &format!(#format_str, self.#index) + } + } else { + quote! { + &self.#index + } }; respan(tokens, &field.ty) })