diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c225e6f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,47 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "types-rs" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..53ed2f4 --- /dev/null +++ b/Cargo.toml @@ -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" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..15043c6 --- /dev/null +++ b/src/lib.rs @@ -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, +} + +impl Parse for Omit { + fn parse(input: ParseStream) -> syn::Result { + let name = input.parse()?; + input.parse::()?; + + 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::()?; + } + 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() + } +} diff --git a/tests/omit.rs b/tests/omit.rs new file mode 100644 index 0000000..4119114 --- /dev/null +++ b/tests/omit.rs @@ -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 { + // a: T, + // } + // _ = NewFoo5 { a: 1 }; + //} +}