typeshare_annotation/
lib.rs

1//! Defines the `#[typeshare]` attribute.
2
3extern crate proc_macro;
4use proc_macro::TokenStream;
5use quote::ToTokens;
6use syn::{parse, Attribute, Data, DeriveInput, Fields};
7
8/// Marks a type as a type shared across the FFI boundary using typeshare.
9///
10/// The `typeshare` program will process all the types with this attribute. It will ignore all types
11/// that do not have this attribute.
12///
13/// # Example
14///
15/// ```ignore
16///  use typeshare::typeshare;
17///
18///  #[typeshare]
19///  pub struct Person {
20///      name: String,
21///      email: String,
22///  }
23/// ```
24///
25/// If the file above is `src/person.rs`, then `typeshare --lang=typescript src/person.rs` would
26/// generate the typescript bindings for the `Person` type.
27///
28/// # Ignoring enum variants
29/// If you don't want to typeshare a certain enum variant, just put a `#[typeshare(skip)]` attribute on
30/// that variant.
31/// ```ignore
32/// use typeshare::typeshare;
33///
34/// #[typeshare]
35/// pub enum SomeEnum {
36///     A,
37///     #[typeshare(skip)]
38///     B, // <- this variant will not be included in the typeshared file(s)
39///     C,
40/// }
41/// ```
42#[proc_macro_attribute]
43pub fn typeshare(_attr: TokenStream, item: TokenStream) -> TokenStream {
44    if let Ok(mut item) = parse::<DeriveInput>(item.clone()) {
45        // We need to remove the #[typeshare] attribute from all data members so the compiler doesn't throw an error.
46        strip_configuration_attribute(&mut item);
47        TokenStream::from(item.to_token_stream())
48    } else {
49        item
50    }
51}
52
53fn strip_configuration_attribute(item: &mut DeriveInput) {
54    fn remove_configuration_from_attributes(attributes: &mut Vec<Attribute>) {
55        const CONFIG_ATTRIBUTE_NAME: &str = "typeshare";
56
57        attributes.retain(|x| x.path().to_token_stream().to_string() != CONFIG_ATTRIBUTE_NAME);
58    }
59
60    fn remove_configuration_from_fields(fields: &mut Fields) {
61        for field in fields.iter_mut() {
62            remove_configuration_from_attributes(&mut field.attrs);
63        }
64    }
65
66    match item.data {
67        Data::Enum(ref mut data_enum) => {
68            for variant in data_enum.variants.iter_mut() {
69                remove_configuration_from_attributes(&mut variant.attrs);
70                remove_configuration_from_fields(&mut variant.fields);
71            }
72        }
73        Data::Struct(ref mut data_struct) => {
74            remove_configuration_from_fields(&mut data_struct.fields);
75        }
76        Data::Union(ref mut data_union) => {
77            for field in data_union.fields.named.iter_mut() {
78                remove_configuration_from_attributes(&mut field.attrs);
79            }
80        }
81    };
82}