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}