From b5c98a35ef4880d970ea08d178a68ab6e7fc5f96 Mon Sep 17 00:00:00 2001 From: currypseudo Date: Sun, 12 Dec 2021 16:11:54 +0800 Subject: [PATCH 1/8] refactor: derive_clone from ExtraAttributes move to Input --- soa-derive-internal/src/input.rs | 13 +++++++------ soa-derive-internal/src/slice.rs | 4 ++-- soa-derive-internal/src/vec.rs | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/soa-derive-internal/src/input.rs b/soa-derive-internal/src/input.rs index c71ff4c..40ccd17 100644 --- a/soa-derive-internal/src/input.rs +++ b/soa-derive-internal/src/input.rs @@ -17,12 +17,12 @@ pub struct Input { /// 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 +35,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 +129,6 @@ impl ExtraAttributes { // always add this derive to the Vec struct self.vec.push(derive); - if path.is_ident("Clone") { - self.derive_clone = true; - } } } @@ -160,6 +156,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 +170,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 +200,7 @@ impl Input { fields: fields, visibility: input.vis, attrs: extra_attrs, + derive_clone, } } diff --git a/soa-derive-internal/src/slice.rs b/soa-derive-internal/src/slice.rs index fb38c7e..656ce3a 100644 --- a/soa-derive-internal/src/slice.rs +++ b/soa-derive-internal/src/slice.rs @@ -214,7 +214,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> { @@ -524,7 +524,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..648aef5 100644 --- a/soa-derive-internal/src/vec.rs +++ b/soa-derive-internal/src/vec.rs @@ -350,7 +350,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 { From 628b9be2c2320da677686cf4a1f2eabb653725df Mon Sep 17 00:00:00 2001 From: currypseudo Date: Sun, 12 Dec 2021 15:47:33 +0800 Subject: [PATCH 2/8] feat: soa_attr for Vec's field --- soa-derive-internal/src/input.rs | 22 ++++++++++++++++++++-- soa-derive-internal/src/vec.rs | 4 ++++ tests/soa_attr.rs | 28 ++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/soa-derive-internal/src/input.rs b/soa-derive-internal/src/input.rs index 40ccd17..64552fb 100644 --- a/soa-derive-internal/src/input.rs +++ b/soa-derive-internal/src/input.rs @@ -12,6 +12,8 @@ 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 @@ -145,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"), }; @@ -201,6 +218,7 @@ impl Input { visibility: input.vis, attrs: extra_attrs, derive_clone, + field_attrs, } } diff --git a/soa-derive-internal/src/vec.rs b/soa-derive-internal/src/vec.rs index 648aef5..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>, )* } diff --git a/tests/soa_attr.rs b/tests/soa_attr.rs index d43b784..9fb523d 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,30 @@ fn eq_test() { }; assert_eq!(particles0, particles1); } + +#[derive(StructOfArray)] +#[soa_derive(PartialEq, Debug, Serialize, Deserialize)] +pub struct Point { + pub x: f32, + pub y: f32, + #[soa_attr(Vec, serde(skip))] + 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![] + }); + Ok(()) +} From 59b1f0e1d14fcca4a95dd290720744306ed98554 Mon Sep 17 00:00:00 2001 From: currypseudo Date: Sun, 12 Dec 2021 16:00:56 +0800 Subject: [PATCH 3/8] feat: soa_attr for Slice/SliceMut's field --- soa-derive-internal/src/slice.rs | 8 ++++++++ tests/soa_attr.rs | 13 +++++++++++++ 2 files changed, 21 insertions(+) diff --git a/soa-derive-internal/src/slice.rs b/soa-derive-internal/src/slice.rs index 656ce3a..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], )* } @@ -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], )* } diff --git a/tests/soa_attr.rs b/tests/soa_attr.rs index 9fb523d..46809ff 100644 --- a/tests/soa_attr.rs +++ b/tests/soa_attr.rs @@ -30,7 +30,9 @@ fn eq_test() { #[derive(StructOfArray)] #[soa_derive(PartialEq, Debug, Serialize, Deserialize)] pub struct Point { + #[soa_attr(Slice, deprecated)] pub x: f32, + #[soa_attr(SliceMut, deprecated)] pub y: f32, #[soa_attr(Vec, serde(skip))] pub meta: bool @@ -51,5 +53,16 @@ fn serde_skip_test() -> Result<(), serde_json::Error> { 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]; // Should not have a deprecate warning + } + { + let slice = soa.as_mut_slice(); + let _ = slice.y[0]; // Should have a deprecate warning + let _ = slice.x[0]; // Should not have a deprecate warning + + } Ok(()) } From 392d0c91461133bfa3bb3697911496f32ada42de Mon Sep 17 00:00:00 2001 From: currypseudo Date: Sun, 12 Dec 2021 16:06:14 +0800 Subject: [PATCH 4/8] feat: soa_attr for Ref/RefMut's field --- soa-derive-internal/src/refs.rs | 8 ++++++++ tests/soa_attr.rs | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) 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/tests/soa_attr.rs b/tests/soa_attr.rs index 46809ff..7bd74d3 100644 --- a/tests/soa_attr.rs +++ b/tests/soa_attr.rs @@ -31,10 +31,12 @@ fn eq_test() { #[soa_derive(PartialEq, Debug, Serialize, Deserialize)] pub struct Point { #[soa_attr(Slice, deprecated)] + #[soa_attr(RefMut, deprecated)] pub x: f32, #[soa_attr(SliceMut, deprecated)] pub y: f32, #[soa_attr(Vec, serde(skip))] + #[soa_attr(Ref, deprecated)] pub meta: bool } @@ -53,16 +55,27 @@ fn serde_skip_test() -> Result<(), serde_json::Error> { 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]; // Should not have a deprecate warning + let _ = slice.meta[0]; // Should not have a deprecate warning + + let ref_ = slice.get(1).unwrap(); + let _ = ref_.x; // Should not have a deprecate warning + let _ = ref_.y; // Should not have a deprecate warning + let _ = ref_.meta; // Should have a deprecate warning } { - let slice = soa.as_mut_slice(); + let mut slice = soa.as_mut_slice(); let _ = slice.y[0]; // Should have a deprecate warning let _ = slice.x[0]; // Should not have a deprecate warning + let ref_mut = slice.get_mut(1).unwrap(); + let _ = ref_mut.x; // Should have a deprecate warning + let _ = ref_mut.y; // Should not have a deprecate warning + let _ = ref_mut.meta; // Should not have a deprecate warning } Ok(()) } From 63f2157b29fd206cbc986890f2469a18ff93d75e Mon Sep 17 00:00:00 2001 From: currypseudo Date: Sun, 12 Dec 2021 16:10:39 +0800 Subject: [PATCH 5/8] feat: soa_attr for Ptr/PtrMut's field --- soa-derive-internal/src/ptr.rs | 8 ++++++++ tests/soa_attr.rs | 28 +++++++++++++++++++++------- 2 files changed, 29 insertions(+), 7 deletions(-) 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/tests/soa_attr.rs b/tests/soa_attr.rs index 7bd74d3..69caa85 100644 --- a/tests/soa_attr.rs +++ b/tests/soa_attr.rs @@ -34,9 +34,11 @@ pub struct Point { #[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 } @@ -59,23 +61,35 @@ fn serde_skip_test() -> Result<(), serde_json::Error> { { let slice = soa.as_slice(); let _ = slice.x[0]; // Should have a deprecate warning - let _ = slice.y[0]; // Should not have a deprecate warning - let _ = slice.meta[0]; // Should not have a deprecate warning + let _ = slice.y[0]; + let _ = slice.meta[0]; let ref_ = slice.get(1).unwrap(); - let _ = ref_.x; // Should not have a deprecate warning - let _ = ref_.y; // Should not have a deprecate warning + 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.x[0]; // Should not have a deprecate warning + let _ = slice.meta[0]; let ref_mut = slice.get_mut(1).unwrap(); let _ = ref_mut.x; // Should have a deprecate warning - let _ = ref_mut.y; // Should not have a deprecate warning - let _ = ref_mut.meta; // Should not have a deprecate warning + let _ = ref_mut.y; + let _ = ref_mut.meta; + + let ptr_mut = ref_mut.as_ptr(); + let _ = ptr_mut.x; // Should not have a deprecate warning + let _ = ptr_mut.y; // Should not have a deprecate warning + let _ = ptr_mut.meta; // Should have a deprecate warning } Ok(()) } From 5ec3a4205198cb01de7204ba33ae5da15efc6157 Mon Sep 17 00:00:00 2001 From: currypseudo Date: Sun, 12 Dec 2021 16:14:24 +0800 Subject: [PATCH 6/8] fix: comment in soa_attr test --- tests/soa_attr.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/soa_attr.rs b/tests/soa_attr.rs index 69caa85..6f3a9ef 100644 --- a/tests/soa_attr.rs +++ b/tests/soa_attr.rs @@ -87,9 +87,9 @@ fn serde_skip_test() -> Result<(), serde_json::Error> { let _ = ref_mut.meta; let ptr_mut = ref_mut.as_ptr(); - let _ = ptr_mut.x; // Should not have a deprecate warning - let _ = ptr_mut.y; // Should not have a deprecate warning - let _ = ptr_mut.meta; // Should have a deprecate warning + let _ = ptr_mut.x; + let _ = ptr_mut.y; // Should have a deprecate warning + let _ = ptr_mut.meta; } Ok(()) } From 73e0f24e4cc9004b3f344fc2fbdc8f2fca5c1f38 Mon Sep 17 00:00:00 2001 From: currypseudo Date: Sun, 12 Dec 2021 16:15:32 +0800 Subject: [PATCH 7/8] fix: ptr mut deprecated test --- tests/soa_attr.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/soa_attr.rs b/tests/soa_attr.rs index 6f3a9ef..e2a5881 100644 --- a/tests/soa_attr.rs +++ b/tests/soa_attr.rs @@ -81,15 +81,15 @@ fn serde_skip_test() -> Result<(), serde_json::Error> { let _ = slice.y[0]; // Should have a deprecate warning let _ = slice.meta[0]; - let ref_mut = slice.get_mut(1).unwrap(); + 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_ptr(); + let ptr_mut = ref_mut.as_mut_ptr(); let _ = ptr_mut.x; - let _ = ptr_mut.y; // Should have a deprecate warning - let _ = ptr_mut.meta; + let _ = ptr_mut.y; + let _ = ptr_mut.meta; // Should have a deprecate warning } Ok(()) } From 881c49186817939d366b4adf8cb86d32f9c4803c Mon Sep 17 00:00:00 2001 From: currypseudo Date: Wed, 15 Dec 2021 14:38:36 +0800 Subject: [PATCH 8/8] doc: mention #44 about new feature --- README.md | 22 ++++++++++++++++++++++ src/lib.rs | 21 +++++++++++++++++++++ 2 files changed, 43 insertions(+) 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/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