Skip to content

Commit

Permalink
Add support for std on containers and variants (fixes #5)
Browse files Browse the repository at this point in the history
  • Loading branch information
udoprog committed May 18, 2024
1 parent 53f13cc commit b79a7b6
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 21 deletions.
66 changes: 55 additions & 11 deletions crates/borrowme-macros/src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ pub(crate) struct Container {
pub(crate) owned_ident: Option<(Span, syn::Ident)>,
/// Attributes to apply.
pub(crate) attributes: Attributes,
/// Default field type kind.
pub(crate) kind: Option<(Span, FieldTypeKind)>,
}

impl Container {
Expand All @@ -53,25 +55,34 @@ pub(crate) fn container(
let mut attr = Container {
owned_ident: None,
attributes: Attributes::default(),
kind: None,
};

macro_rules! set_attr {
($field:ident $(. $field2:ident)*, $meta:expr, $value:expr, $message:expr $(,)?) => {
set_attr(cx, &mut attr.$field$(.$field2)*, $meta, $value, $message)
}
}

for a in attrs.iter().chain(rest) {
let result = if a.path().is_ident(BORROWME) {
a.parse_nested_meta(|meta| {
let span = meta.path.span();

if meta.path.is_ident("name") {
meta.input.parse::<Token![=]>()?;
set_attr(
cx,
&mut attr.owned_ident,
meta.path.span(),
meta.input.parse()?,
"Duplicate name.",
);
set_attr!(owned_ident, span, meta.input.parse()?, "Duplicate name.",);
return Ok(());
}

if meta.path.is_ident("std") {
let kind = FieldTypeKind::Std;
set_attr!(kind, span, kind, "Duplicate container field kind.");
return Ok(());
}

Err(syn::Error::new(
meta.path.span(),
span,
format_args!("#[{BORROWME}]: Unsupported attribute."),
))
})
Expand Down Expand Up @@ -99,19 +110,39 @@ pub(crate) fn container(

pub(crate) struct Variant {
pub(crate) attributes: Attributes,
pub(crate) kind: Option<(Span, FieldTypeKind)>,
}

/// Parse variant attributes.
pub(crate) fn variant(cx: &Ctxt, attrs: &[syn::Attribute]) -> Result<Variant, ()> {
pub(crate) fn variant(
cx: &Ctxt,
attrs: &[syn::Attribute],
container: &Container,
) -> Result<Variant, ()> {
let mut variant = Variant {
attributes: Attributes::default(),
kind: None,
};

macro_rules! set_attr {
($field:ident $(. $field2:ident)*, $meta:expr, $value:expr, $message:expr $(,)?) => {
set_attr(cx, &mut variant.$field$(.$field2)*, $meta, $value, $message)
}
}

for a in attrs {
let result = if a.path().is_ident(BORROWME) {
a.parse_nested_meta(|meta| {
let span = meta.path.span();

if meta.path.is_ident("std") {
let kind = FieldTypeKind::Std;
set_attr!(kind, span, kind, "Duplicate variant field kind.");
return Ok(());
}

Err(syn::Error::new(
meta.path.span(),
span,
format_args!("#[{BORROWME}]: Unsupported attribute."),
))
})
Expand All @@ -134,6 +165,10 @@ pub(crate) fn variant(cx: &Ctxt, attrs: &[syn::Attribute]) -> Result<Variant, ()
}
}

if variant.kind.is_none() {
variant.kind = container.kind;
}

Ok(variant)
}

Expand Down Expand Up @@ -219,7 +254,12 @@ impl Field {
/// We provide `field_spans` so that the processed `FieldType::Type` can be
/// respanned to emit better diagnostics in case if fails something like a type
/// check.
pub(crate) fn field(cx: &Ctxt, spans: (Span, Span), attrs: &[syn::Attribute]) -> Result<Field, ()> {
pub(crate) fn field(
cx: &Ctxt,
spans: (Span, Span),
attrs: &[syn::Attribute],
default_kind: Option<(Span, FieldTypeKind)>,
) -> Result<Field, ()> {
let mut attr = Field {
is_mut: None,
ty: FieldType::default(),
Expand Down Expand Up @@ -376,6 +416,10 @@ pub(crate) fn field(cx: &Ctxt, spans: (Span, Span), attrs: &[syn::Attribute]) ->
}
}

if attr.ty.kind.is_none() {
attr.ty.kind = default_kind;
}

Ok(attr)
}

Expand Down
7 changes: 5 additions & 2 deletions crates/borrowme-macros/src/implement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ pub(crate) fn implement(
process_fields(
cx,
Access::SelfAccess,
attr.kind,
&mut o_st.fields,
&mut b_st.fields,
&mut to_owned_entries,
Expand Down Expand Up @@ -250,7 +251,7 @@ pub(crate) fn implement(
let borrow_ident = b_en.ident.clone();

for (o_variant, b_variant) in o_en.variants.iter_mut().zip(b_en.variants.iter_mut()) {
let attr = attr::variant(cx, &o_variant.attrs)?;
let attr = attr::variant(cx, &o_variant.attrs, &attr)?;
attr::strip([&mut o_variant.attrs, &mut b_variant.attrs]);

apply_attributes(&attr.attributes, &mut o_variant.attrs, &mut b_variant.attrs);
Expand All @@ -261,6 +262,7 @@ pub(crate) fn implement(
process_fields(
cx,
Access::BindingAccess,
attr.kind,
&mut o_variant.fields,
&mut b_variant.fields,
&mut to_owned_entries,
Expand Down Expand Up @@ -419,6 +421,7 @@ pub(crate) fn implement(
fn process_fields(
cx: &Ctxt,
access: Access,
default_kind: Option<(Span, attr::FieldTypeKind)>,
o_fields: &mut syn::Fields,
b_fields: &mut syn::Fields,
to_owned_entries: &mut Vec<syn::FieldValue>,
Expand All @@ -428,7 +431,7 @@ fn process_fields(
for (index, (o_field, b_field)) in o_fields.iter_mut().zip(b_fields.iter_mut()).enumerate() {
let field_ty_spans = field_ty_spans(o_field);

let mut attr = attr::field(cx, field_ty_spans, &o_field.attrs)?;
let mut attr = attr::field(cx, field_ty_spans, &o_field.attrs, default_kind)?;
attr::strip([&mut o_field.attrs, &mut b_field.attrs]);
apply_attributes(&attr.attributes, &mut o_field.attrs, &mut b_field.attrs);

Expand Down
2 changes: 2 additions & 0 deletions crates/borrowme-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
//! [<img alt="crates.io" src="https://img.shields.io/crates/v/borrowme-macros.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/borrowme-macros)
//! [<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-borrowme--macros-66c2a5?style=for-the-badge&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/borrowme-macros)

#![allow(clippy::too_many_arguments)]

mod attr;
mod ctxt;
mod implement;
Expand Down
81 changes: 73 additions & 8 deletions crates/borrowme/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,9 @@
///
/// This section documents supported container attributes:
///
/// * [`#[borrowme(std)]`][container-std] which acts as if
/// [`#[borrowme(std)]`][std] is applied to every field and variant in the
/// container by default.
/// * [`#[borrowme(name = <ident>)]`][name] which is used to change the name of
/// the generated *owned* variant.
/// * [`#[borrowed_attr(<meta>)]`][b-c] and [`#[owned_attr(<meta>)]`][o-c] which
Expand Down Expand Up @@ -250,6 +253,37 @@
///
/// <br>
///
/// #### `#[borrowme(std)]` container attribute
///
/// This container attribute acts as if [`#[borrowme(std)]`][std] is applied to
/// every field or variant in the container.
///
/// Note that this defeats copy and reference heuristics.
///
/// ```
/// use borrowme::borrowme;
///
/// #[borrowme]
/// #[borrowme(std)]
/// struct StdStruct<'a> {
/// a: &'a String,
/// #[borrowme(copy)]
/// b: u32,
/// }
///
/// #[borrowme]
/// #[borrowme(std)]
/// enum StdEnum<'a> {
/// Variant {
/// a: &'a String,
/// #[borrowme(copy)]
/// b: u32,
/// }
/// }
/// ```
///
/// <br>
///
/// #### `#[borrowme(name = <ident>)]` container attribute
///
/// This allows you to pick the name to use for the generated type. By default
Expand Down Expand Up @@ -328,6 +362,9 @@
///
/// * [`#[borrowed_attr(<meta>)]`][b-v] and [`#[owned_attr(<meta>)]`][o-v] which
/// are used to add custom attributes.
/// * [`#[borrowme(std)]`][variant-std] which acts as if
/// [`#[borrowme(std)]`][std] is applied to every field in the variant by
/// default.
///
/// Variant attributes are attributes which apply to `enum` variants.
///
Expand Down Expand Up @@ -375,6 +412,32 @@
///
/// <br>
///
/// #### `#[borrowme(std)]` variant attribute
///
/// This container attribute acts as if [`#[borrowme(std)]`][std] is applied to
/// every field inside of a variant.
///
/// Note that this defeats copy and reference heuristics.
///
/// ```
/// use borrowme::borrowme;
///
/// #[borrowme]
/// enum StdEnum<'a> {
/// #[borrowme(std)]
/// Variant {
/// a: &'a String,
/// #[borrowme(copy)]
/// b: u32,
/// }
/// }
/// ```
///
/// <br>
///
///
/// <br>
///
/// ## Field attributes
///
/// This section documents supported field attributes:
Expand Down Expand Up @@ -756,20 +819,22 @@
/// }
/// ```
///
/// [name]: #borrowmename--ident-container-attribute
/// [b-c]: #borrowed_attrmeta-container-attribute
/// [o-c]: #owned_attrmeta-container-attribute
/// [b-f]: #borrowed_attrmeta-field-attribute
/// [b-v]: #borrowed_attrmeta-variant-attribute
/// [o-v]: #owned_attrmeta-variant-attribute
/// [owned]: #ownedtype-or-borrowmeowned--type-field-attributes
/// [to_owned_with]: #borrowmeto_owned_with--path-field-attribute
/// [borrow_with]: #borrowmeborrow_with--path-field-attribute
/// [with]: #borrowmewith--path-field-attribute
/// [container-std]: #borrowmestd-container-attribute
/// [copy]: #copy-and-no_copy-field-attribute
/// [std]: #borrowmestd-field-attribute
/// [mut]: #borrowmemut-field-attribute
/// [b-f]: #borrowed_attrmeta-field-attribute
/// [name]: #borrowmename--ident-container-attribute
/// [o-c]: #owned_attrmeta-container-attribute
/// [o-f]: #owned_attrmeta-field-attribute
/// [o-v]: #owned_attrmeta-variant-attribute
/// [owned]: #ownedtype-or-borrowmeowned--type-field-attributes
/// [std]: #borrowmestd-field-attribute
/// [to_owned_with]: #borrowmeto_owned_with--path-field-attribute
/// [variant-std]: #borrowmestd-variant-attribute
/// [with]: #borrowmewith--path-field-attribute
#[doc(inline)]
pub use borrowme_macros::borrowme;

Expand Down
32 changes: 32 additions & 0 deletions crates/borrowme/tests/std_containers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use borrowme::borrowme;

#[borrowme]
#[borrowme(std)]
#[derive(Clone)]
struct StdStruct<'a> {
a: &'a String,
#[borrowme(copy)]
b: u32,
}

#[borrowme]
#[borrowme(std)]
#[derive(Clone)]
enum StdOnEnum<'a> {
Variant {
a: &'a String,
#[borrowme(copy)]
b: u32,
},
}

#[borrowme]
#[derive(Clone)]
enum StdVariant<'a> {
#[borrowme(std)]
Variant {
a: &'a String,
#[borrowme(copy)]
b: u32,
},
}

0 comments on commit b79a7b6

Please sign in to comment.