Skip to content

Commit

Permalink
feat: add omit macro
Browse files Browse the repository at this point in the history
  • Loading branch information
skanehira committed Jan 25, 2024
1 parent 6d821fd commit 94c30af
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
47 changes: 47 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "types-rs"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
proc-macro = true

[dependencies]
quote = "1.0.35"
syn = { version = "2.0.48", features = ["full", "extra-traits"] }
proc-macro2 = "1.0.78"
74 changes: 74 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use std::collections::HashMap;

use proc_macro::TokenStream;
use quote::quote;
use syn::{
bracketed,
parse::{Parse, ParseStream},
parse_macro_input, Ident, ItemStruct, Token,
};

#[allow(dead_code)]
#[derive(Debug)]
struct Omit {
name: Ident,
fields: HashMap<Ident, ()>,
}

impl Parse for Omit {
fn parse(input: ParseStream) -> syn::Result<Self> {
let name = input.parse()?;
input.parse::<Token![,]>()?;

let fields = {
let content;
bracketed!(content in input);

let mut fields = HashMap::new();
while !content.is_empty() {
fields.insert(content.parse()?, ());
if content.is_empty() {
break;
}
content.parse::<Token![,]>()?;
}
fields
};

Ok(Omit { name, fields })
}
}

#[proc_macro_attribute]
pub fn omit(attr: TokenStream, item: TokenStream) -> TokenStream {
let attr = parse_macro_input!(attr as Omit);
let struct_name = attr.name;

let item = parse_macro_input!(item as ItemStruct);
let fields: Vec<_> = item
.fields
.into_iter()
.filter_map(|field| {
if let Some(ref ident) = field.ident {
if attr.fields.contains_key(ident) {
return None;
}
};
Some(field)
})
.collect();

if fields.is_empty() {
quote! {
struct #struct_name;
}
.into()
} else {
quote! {
struct #struct_name {
#(#fields),*
}
}
.into()
}
}
55 changes: 55 additions & 0 deletions tests/omit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#![allow(dead_code)]
use types_rs::omit;

#[test]
fn test() {
// omit partial field
{
#[omit(NewFoo, [b])]
struct Foo {
a: i32,
b: &str,
}
_ = NewFoo { a: 1 };
}

// omit all fields
{
#[omit(NewFoo2, [a, b])]
struct Foo {
a: i32,
b: &str,
}
_ = NewFoo2;
}

// skip non-existent field
{
#[omit(NewFoo3, [c])]
struct Foo {
a: i32,
b: &'static str,
}
_ = NewFoo3 {
a: 1,
b: "2",
};
}

// omit unit struct
{
#[omit(NewFoo4, [])]
struct Foo;
_ = NewFoo4;
}

// TODO: support generics
// omit generic struct
//{
// #[omit(NewFoo5, [])]
// struct Foo<T> {
// a: T,
// }
// _ = NewFoo5 { a: 1 };
//}
}

0 comments on commit 94c30af

Please sign in to comment.