Skip to content

Commit

Permalink
rid-macro: adding ability to skip a struct field (#26)
Browse files Browse the repository at this point in the history
- introduced `#[derive(rid::Config)]` which adds rid config attributes
  to the model struct it is applied to
- `#[derive(rid::Config)]` itself does not add any generated code, but
  just serves to introduce the attrs
- those attrs are used to configure each struct field if needed, so far
  `#[rid(skip)]` to omit it from rendering FFI wrapper code
- attr syntax modeled after
  [serde(skip)](https://serde.rs/field-attrs.html#skip)
- as with serde attrs `rid(*)` are extensible for future needs

NOTE: I tried to opaquely add this `derive` to each model struct, but
caused problems when another proc_macro attribute comes after (derive
attrs need to be last). In the future we could explore rewriting the
TokenStream and inject the derive in the right place so the user doesn't
have to add it explicitly.
  • Loading branch information
thlorenz authored Sep 6, 2021
1 parent e87a757 commit 61f9768
Show file tree
Hide file tree
Showing 10 changed files with 142 additions and 5 deletions.
6 changes: 6 additions & 0 deletions rid-macro-impl/src/attrs/config_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ impl EnumConfig {
"cannot have rid::export attribute on enums"
);
}
RidAttr::Rid(attr_ident, _) => {
abort!(
attr_ident,
"cannot have rid() config attributes on enums"
);
}
RidAttr::DeriveDebug(_) => debug = true,
}
}
Expand Down
6 changes: 6 additions & 0 deletions rid-macro-impl/src/attrs/config_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ impl FunctionConfig {
is_exported = true;
fn_export_alias = name.clone();
}
RidAttr::Rid(attr_ident, _) => {
abort!(
attr_ident,
"cannot have config rid() attributes on function exports"
);
}
// The below are invalid on a function but already checked by Rust itself
RidAttr::DeriveDebug(_) => {}
}
Expand Down
6 changes: 6 additions & 0 deletions rid-macro-impl/src/attrs/config_impl_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ impl ImplBlockConfig {
is_exported = true;
}
}
RidAttr::Rid(attr_ident, _) => {
abort!(
attr_ident,
"cannot have config rid() attributes on impl blocks"
);
}
// The below are invalid on an impl block but already checked by Rust itself
RidAttr::DeriveDebug(_) => {}
}
Expand Down
6 changes: 6 additions & 0 deletions rid-macro-impl/src/attrs/config_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ impl StructConfig {
"cannot have rid::export attribute on structs"
);
}
RidAttr::Rid(attr_ident, _) => {
abort!(
attr_ident,
"cannot have config rid() attributes on structs only on its fields"
);
}
RidAttr::DeriveDebug(_) => debug = true,
}
}
Expand Down
38 changes: 36 additions & 2 deletions rid-macro-impl/src/attrs/parse_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,39 @@ use syn::{

use crate::common::abort;

const RID_CONFIG_SKIP: &str = "skip";
const RID_CONFIGS: &[&str; 1] = &[RID_CONFIG_SKIP];

#[derive(Debug, PartialEq, Clone)]
pub enum RidAttr {
// rid specific
// -----------------
// Rid Specific
// -----------------
Structs(Ident, Vec<syn::Ident>),
Enums(Ident, Vec<syn::Ident>),
Message(Ident, syn::Ident),
Export(Ident, Option<Ident>),

// derives
// Rid Config Attributes
Rid(Ident, Vec<syn::Ident>),

// -----------------
// Derives
// -----------------
DeriveDebug(Ident),
}

impl RidAttr {
pub fn has_skip(&self) -> bool {
match self {
RidAttr::Rid(_, idents) => idents
.iter()
.any(|x| x.to_string().as_str() == RID_CONFIG_SKIP),
_ => false,
}
}
}

#[derive(Debug)]
struct AttrTuple {
paren_token: token::Paren,
Expand Down Expand Up @@ -160,6 +181,19 @@ fn parse_segments(
None
}
}
"rid" => {
let idents = idents_from_nested(nested);
for ident in &idents {
if !RID_CONFIGS.contains(&ident.to_string().as_str()) {
abort!(
ident,
"Only rid({}) are valid",
RID_CONFIGS.join(", ")
);
}
}
Some(RidAttr::Rid(first.clone(), idents))
}
_ => None,
}
} else {
Expand Down
6 changes: 6 additions & 0 deletions rid-macro-impl/src/message/config_message_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ impl MessageEnumConfig {
"cannot have rid::export attribute on enums"
);
}
RidAttr::Rid(attr_ident, _) => {
abort!(
attr_ident,
"cannot have config rid() attributes on message enum"
);
}
RidAttr::DeriveDebug(_) => debug = true,
}
}
Expand Down
4 changes: 4 additions & 0 deletions rid-macro-impl/src/model/field_access/render_field_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ impl ParsedStruct {
rust_config: &RenderRustAccessConfig,
dart_config: &RenderDartAccessConfig,
) -> (TokenStream, String) {
if self.fields.is_empty() {
return (TokenStream::new(), String::new());
}

let RenderRustFieldAccessResult {
tokens: rust_tokens,
collection_accesses,
Expand Down
49 changes: 49 additions & 0 deletions rid-macro-impl/src/model/field_access/render_field_access_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -661,3 +661,52 @@ mod struct_field_access_single_hash_map_u8_u8 {
// assert_eq!(tokens.to_string().trim(), expected.to_string().trim());
}
}

mod struct_field_access_rid_skip {
use crate::common::dump_tokens;

use super::*;

#[test]
fn single_skipped_field() {
let input: TokenStream = quote! {
#[derive(rid::Config)]
struct MyStruct {
#[rid(skip)]
skip_me: SystemTime
}
};
let tokens = render_rust_field_access(input);
// Renders nothing since all fields are skiped
let expected = TokenStream::new();
assert_eq!(tokens.to_string().trim(), expected.to_string().trim());
}

#[test]
fn two_fields_one_skipped() {
let input: TokenStream = quote! {
#[derive(rid::Config)]
struct MyStruct {
#[rid(skip)]
skip_me: SystemTime,
but_not_me: u32
}
};
let expected = quote! {
mod __my_struct_field_access {
use super::*;
fn rid_mystruct_but_not_me(ptr: *mut MyStruct) -> u32 {
let receiver = unsafe {
assert!(!ptr.is_null());
let ptr: *mut MyStruct = &mut *ptr;
ptr.as_mut().expect("resolve_ptr.as_mut failed")
};
receiver.but_not_me
}
}
};

let tokens = render_rust_field_access(input);
assert_eq!(tokens.to_string().trim(), expected.to_string().trim());
}
}
17 changes: 14 additions & 3 deletions rid-macro-impl/src/parse/parsed_struct.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use syn::{Ident, ItemStruct};
use syn::{Field, Ident, ItemStruct};

use crate::attrs::{raw_typedef_ident, Category, StructConfig, TypeInfoMap};
use crate::{
attrs::{raw_typedef_ident, Category, RidAttr, StructConfig, TypeInfoMap},
parse_rid_attrs,
};

use super::{rust_type::RustType, ParsedStructField};

Expand All @@ -19,7 +22,10 @@ impl ParsedStruct {
let fields = item
.fields
.iter()
.map(|f| ParsedStructField::new(f, &config.type_infos))
.filter_map(|f| match should_include_field(f) {
true => Some(ParsedStructField::new(f, &config.type_infos)),
fale => None,
})
.collect();
Self {
ident,
Expand All @@ -34,3 +40,8 @@ impl ParsedStruct {
&self.config.type_infos
}
}

fn should_include_field(f: &Field) -> bool {
let parsed_attrs = parse_rid_attrs(&f.attrs);
!parsed_attrs.iter().any(RidAttr::has_skip)
}
9 changes: 9 additions & 0 deletions rid-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,15 @@ pub fn enums(_attrs: TokenStream, input: TokenStream) -> TokenStream {
input
}

// -----------------
// #[derive(rid::Config)]
// -----------------
#[proc_macro_derive(Config, attributes(rid))]
#[proc_macro_error]
pub fn rid_attr(_input: TokenStream) -> TokenStream {
TokenStream::new()
}

// -----------------
// #[derive(rid::Display)]
// -----------------
Expand Down

0 comments on commit 61f9768

Please sign in to comment.