diff --git a/README.md b/README.md index ffc7dde..ebca367 100644 --- a/README.md +++ b/README.md @@ -59,10 +59,15 @@ If you want to add attribute to a specific generated struct(such as attribute `#[soa_attr(Vec, cfg_attr(test, derive(PartialEq)))]` to the struct declaration. +`soa_attr` could also be added to specific generated struct's field. + +For example: + ```rust #[derive(Debug, PartialEq, StructOfArray)] #[soa_attr(Vec, cfg_attr(test, derive(PartialEq)))] pub struct Cheese { + #[soa_attr(Vec, deprecated)] pub smell: f64, pub color: (f64, f64, f64), pub with_mushrooms: bool, @@ -70,6 +75,20 @@ pub struct Cheese { } ``` +will generate the struct like: + +```rust +#[cfg_attr(test, derive(PartialEq))] +pub struct CheeseVec { + #[deprecated] + pub smell: Vec, + pub color: Vec<(f64, f64, f64)>, + pub with_mushrooms: Vec, + pub name: Vec, +} +``` + + Mappings for first argument of ``soa_attr`` to the generated struct for ``Cheese``: * `Vec` => `CheeseVec` * `Slice` => `CheeseSlice` @@ -79,6 +98,9 @@ Mappings for first argument of ``soa_attr`` to the generated struct for ``Cheese * `Ptr` => `CheesePtr` * `PtrMut` => `CheesePtrMut` + +To be mentioned, there is an [issue](https://github.com/lumol-org/soa-derive/issues/44) related to `#[soa_attr(Vec, serde(skip))]` on field. + ## Usage and API All the generated code have some generated documentation with it, so you diff --git a/soa-derive-internal/src/input.rs b/soa-derive-internal/src/input.rs index c71ff4c..64552fb 100644 --- a/soa-derive-internal/src/input.rs +++ b/soa-derive-internal/src/input.rs @@ -12,17 +12,19 @@ pub struct Input { pub name: Ident, /// The list of fields in the struct pub fields: Vec, + /// Additional attributes requested with `#[soa_attr(...)]` on fields + pub field_attrs: Vec, /// The struct overall visibility pub visibility: Visibility, /// Additional attributes requested with `#[soa_attr(...)]` or /// `#[soa_derive()]` pub attrs: ExtraAttributes, -} -pub struct ExtraAttributes { // did the user explicitly asked us to derive clone? pub derive_clone: bool, +} +pub struct ExtraAttributes { pub vec: Vec, pub slice: Vec, pub slice_mut: Vec, @@ -35,7 +37,6 @@ pub struct ExtraAttributes { impl ExtraAttributes { fn new() -> ExtraAttributes { ExtraAttributes { - derive_clone: false, vec: Vec::new(), slice: Vec::new(), slice_mut: Vec::new(), @@ -130,9 +131,6 @@ impl ExtraAttributes { // always add this derive to the Vec struct self.vec.push(derive); - if path.is_ident("Clone") { - self.derive_clone = true; - } } } @@ -149,8 +147,23 @@ fn create_derive_meta(path: Path) -> Meta { impl Input { pub fn new(input: DeriveInput) -> Input { - let fields = match input.data { - Data::Struct(s) => s.fields.iter().cloned().collect::>(), + let mut fields = Vec::new(); + let mut field_attrs = Vec::new(); + match input.data { + Data::Struct(s) => { + for field in s.fields.iter() { + let mut extra_attrs = ExtraAttributes::new(); + fields.push(field.clone()); + for attr in &field.attrs { + if let Ok(meta) = attr.parse_meta() { + if meta.path().is_ident("soa_attr") { + extra_attrs.parse(&meta); + } + } + } + field_attrs.push(extra_attrs); + } + } _ => panic!("#[derive(StructOfArray)] only supports struct"), }; @@ -160,6 +173,7 @@ impl Input { let mut extra_attrs = ExtraAttributes::new(); + let mut derive_clone = false; for attr in input.attrs { if let Ok(meta) = attr.parse_meta() { if meta.path().is_ident("soa_derive") { @@ -173,6 +187,9 @@ impl Input { panic!("can not derive Copy for SoA vectors"); } extra_attrs.add_derive(path); + if path.is_ident("Clone") { + derive_clone = true; + } } NestedMeta::Lit(_) => { panic!( @@ -200,6 +217,8 @@ impl Input { fields: fields, visibility: input.vis, attrs: extra_attrs, + derive_clone, + field_attrs, } } diff --git a/soa-derive-internal/src/ptr.rs b/soa-derive-internal/src/ptr.rs index 9c041bb..adcbd64 100644 --- a/soa-derive-internal/src/ptr.rs +++ b/soa-derive-internal/src/ptr.rs @@ -38,6 +38,12 @@ pub fn derive(input: &Input) -> TokenStream { .map(|field| format!("A mutable pointer to a `{0}` from a [`{1}`](struct.{1}.html)", field, vec_name)) .collect::>(); + let field_attrs = input.field_attrs.iter() + .map(|attr| &attr.ptr).collect::>(); + + let mut_field_attrs = input.field_attrs.iter() + .map(|attr| &attr.ptr_mut).collect::>(); + quote! { /// An analog of a pointer to #[doc = #doc_url] @@ -47,6 +53,7 @@ pub fn derive(input: &Input) -> TokenStream { #visibility struct #ptr_name { #( #[doc = #fields_doc] + #(#[#field_attrs])* pub #fields_names_1: *const #fields_types, )* } @@ -59,6 +66,7 @@ pub fn derive(input: &Input) -> TokenStream { #visibility struct #ptr_mut_name { #( #[doc = #fields_mut_doc] + #(#[#mut_field_attrs])* pub #fields_names_1: *mut #fields_types, )* } diff --git a/soa-derive-internal/src/refs.rs b/soa-derive-internal/src/refs.rs index ef3aa97..a6076cc 100644 --- a/soa-derive-internal/src/refs.rs +++ b/soa-derive-internal/src/refs.rs @@ -34,6 +34,12 @@ pub fn derive(input: &Input) -> TokenStream { .map(|field| format!("A mutable reference to a `{0}` from a [`{1}`](struct.{1}.html)", field, vec_name)) .collect::>(); + let field_attrs = input.field_attrs.iter() + .map(|attr| &attr.ref_).collect::>(); + + let mut_field_attrs = input.field_attrs.iter() + .map(|attr| &attr.ref_mut).collect::>(); + quote! { /// A reference to a #[doc = #doc_url] @@ -43,6 +49,7 @@ pub fn derive(input: &Input) -> TokenStream { #visibility struct #ref_name<'a> { #( #[doc = #fields_doc] + #(#[#field_attrs])* pub #fields_names_1: &'a #fields_types, )* } @@ -54,6 +61,7 @@ pub fn derive(input: &Input) -> TokenStream { #visibility struct #ref_mut_name<'a> { #( #[doc = #fields_mut_doc] + #(#[#mut_field_attrs])* pub #fields_names_1: &'a mut #fields_types, )* } diff --git a/soa-derive-internal/src/slice.rs b/soa-derive-internal/src/slice.rs index fb38c7e..922965c 100644 --- a/soa-derive-internal/src/slice.rs +++ b/soa-derive-internal/src/slice.rs @@ -39,6 +39,9 @@ pub fn derive(input: &Input) -> TokenStream { .map(|field| format!("A slice of `{0}` from a [`{1}`](struct.{1}.html)", field, vec_name)) .collect::>(); + let field_attrs = input.field_attrs.iter() + .map(|attr| &attr.slice).collect::>(); + let mut generated = quote! { /// A slice of #[doc = #doc_url] @@ -51,6 +54,7 @@ pub fn derive(input: &Input) -> TokenStream { #visibility struct #slice_name<'a> { #( #[doc = #fields_doc] + #(#[#field_attrs])* pub #fields_names: &'a [#fields_types], )* } @@ -214,7 +218,7 @@ pub fn derive(input: &Input) -> TokenStream { } }; - if input.attrs.derive_clone { + if input.derive_clone { generated.append_all(quote!{ #[allow(dead_code)] impl<'a> #slice_name<'a> { @@ -272,6 +276,9 @@ pub fn derive_mut(input: &Input) -> TokenStream { .map(|field| format!("A mutable slice of `{0}` from a [`{1}`](struct.{1}.html)", field, vec_name)) .collect::>(); + let field_attrs = input.field_attrs.iter() + .map(|attr| &attr.slice_mut).collect::>(); + let mut generated = quote! { /// A mutable slice of #[doc = #doc_url] @@ -283,6 +290,7 @@ pub fn derive_mut(input: &Input) -> TokenStream { #visibility struct #slice_mut_name<'a> { #( #[doc = #fields_doc] + #(#[#field_attrs])* pub #fields_names_1: &'a mut [#fields_types], )* } @@ -524,7 +532,7 @@ pub fn derive_mut(input: &Input) -> TokenStream { } }; - if input.attrs.derive_clone { + if input.derive_clone { generated.append_all(quote!{ #[allow(dead_code)] impl<'a> #slice_mut_name<'a> { diff --git a/soa-derive-internal/src/vec.rs b/soa-derive-internal/src/vec.rs index 980ce59..0103c05 100644 --- a/soa-derive-internal/src/vec.rs +++ b/soa-derive-internal/src/vec.rs @@ -35,6 +35,9 @@ pub fn derive(input: &Input) -> TokenStream { let fields_types = &input.fields.iter() .map(|field| &field.ty) .collect::>(); + + let field_attrs = input.field_attrs.iter() + .map(|attr| &attr.vec).collect::>(); let mut generated = quote! { /// An analog to ` @@ -45,6 +48,7 @@ pub fn derive(input: &Input) -> TokenStream { #visibility struct #vec_name { #( #[doc = #fields_doc] + #(#[#field_attrs])* pub #fields_names: Vec<#fields_types>, )* } @@ -350,7 +354,7 @@ pub fn derive(input: &Input) -> TokenStream { } }; - if input.attrs.derive_clone { + if input.derive_clone { generated.append_all(quote!{ #[allow(dead_code)] impl #vec_name { diff --git a/src/lib.rs b/src/lib.rs index 3a173a1..44f9b5e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,12 +57,17 @@ //! attribute `#[soa_attr(Vec, cfg_attr(test, derive(PartialEq)))]` to the //! struct declaration. //! +//! `soa_attr` could also be added to specific generated struct's field. +//! +//! For example: +//! //! ``` //! # #[macro_use] extern crate soa_derive; //! # fn main() { //! #[derive(Debug, PartialEq, StructOfArray)] //! #[soa_attr(Vec, cfg_attr(test, derive(PartialEq)))] //! pub struct Cheese { +//! #[soa_attr(Vec, deprecated)] //! pub smell: f64, //! pub color: (f64, f64, f64), //! pub with_mushrooms: bool, @@ -71,6 +76,19 @@ //! # } //! ``` //! +//! will generate the struct like: +//! +//! ```rust +//! #[cfg_attr(test, derive(PartialEq))] +//! pub struct CheeseVec { +//! #[deprecated] +//! pub smell: Vec, +//! pub color: Vec<(f64, f64, f64)>, +//! pub with_mushrooms: Vec, +//! pub name: Vec, +//! } +//! ``` +//! //! Mappings for first argument of ``soa_attr`` to the generated struct for ``Cheese``: //! * `Vec` => `CheeseVec` //! * `Slice` => `CheeseSlice` @@ -80,6 +98,9 @@ //! * `Ptr` => `CheesePtr` //! * `PtrMut` => `CheesePtrMut` //! +//! +//! To be mentioned, there is an [issue](https://github.com/lumol-org/soa-derive/issues/44) related to `#[soa_attr(Vec, serde(skip))]` on field. +//! //! # Usage and API //! //! All the generated code have some generated documentation with it, so you diff --git a/tests/soa_attr.rs b/tests/soa_attr.rs index d43b784..e2a5881 100644 --- a/tests/soa_attr.rs +++ b/tests/soa_attr.rs @@ -1,3 +1,4 @@ +use serde::{Serialize, Deserialize}; use soa_derive::StructOfArray; #[derive(Debug, Clone, PartialEq, StructOfArray)] @@ -25,3 +26,70 @@ fn eq_test() { }; assert_eq!(particles0, particles1); } + +#[derive(StructOfArray)] +#[soa_derive(PartialEq, Debug, Serialize, Deserialize)] +pub struct Point { + #[soa_attr(Slice, deprecated)] + #[soa_attr(RefMut, deprecated)] + pub x: f32, + #[soa_attr(SliceMut, deprecated)] + #[soa_attr(Ptr, deprecated)] + pub y: f32, + #[soa_attr(Vec, serde(skip))] + #[soa_attr(Ref, deprecated)] + #[soa_attr(PtrMut, deprecated)] + pub meta: bool +} + +#[test] +fn serde_skip_test() -> Result<(), serde_json::Error> { + let mut soa = PointVec::new(); + soa.push(Point { x: 1.0, y: 2.0, meta: true }); + soa.push(Point { x: 3.0, y: 4.0, meta: false }); + + + let json = serde_json::to_string(&soa)?; + assert_eq!(json, r#"{"x":[1.0,3.0],"y":[2.0,4.0]}"#); + let soa2: PointVec = serde_json::from_str(&json)?; + assert_eq!(&soa2, &PointVec { + x: vec![1.0, 3.0], + y: vec![2.0, 4.0], + meta: vec![] + }); + + { + let slice = soa.as_slice(); + let _ = slice.x[0]; // Should have a deprecate warning + let _ = slice.y[0]; + let _ = slice.meta[0]; + + let ref_ = slice.get(1).unwrap(); + let _ = ref_.x; + let _ = ref_.y; + let _ = ref_.meta; // Should have a deprecate warning + + let ptr = ref_.as_ptr(); + let _ = ptr.x; + let _ = ptr.y; // Should have a deprecate warning + let _ = ptr.meta; + + } + { + let mut slice = soa.as_mut_slice(); + let _ = slice.x[0]; + let _ = slice.y[0]; // Should have a deprecate warning + let _ = slice.meta[0]; + + let mut ref_mut = slice.get_mut(1).unwrap(); + let _ = ref_mut.x; // Should have a deprecate warning + let _ = ref_mut.y; + let _ = ref_mut.meta; + + let ptr_mut = ref_mut.as_mut_ptr(); + let _ = ptr_mut.x; + let _ = ptr_mut.y; + let _ = ptr_mut.meta; // Should have a deprecate warning + } + Ok(()) +}