dos_uid_derive/
lib.rs

1/*!
2# UID
3
4A derive macro that implements the [UniqueIdentifier] trait.
5
6## Examples
7
8Setting the data type and port # to the default values: `Vec<f64>` and `50_000u32`, respectively:
9```
10use interface::UID;
11
12#[derive(UID)]
13enum Tag {}
14```
15
16The data type and port # are set with:
17```
18use interface::UID;
19
20struct Q<T>(std::marker::PhantomData<T>);
21
22enum ID {}
23
24#[derive(UID)]
25#[uid(data = Q<ID>, port = 9999)]
26enum TU {}
27```
28
29An alias is a type that implements the [Read], [Write] or [Size] trait of another type that implements the same traits for the same client:
30```
31use interface::{UID, Data, Read, Size, Update, Write};
32# struct Q<T>(std::marker::PhantomData<T>);
33# enum ID {}
34# #[derive(UID)]
35# #[uid(data = Q<ID>, port = 9999)]
36# enum TU {}
37
38struct Client {}
39impl Update for Client {}
40impl Write<TU> for Client {
41    fn write(&mut self) -> Option<Data<TU>> {
42        None
43    }
44}
45impl Read<TU> for Client {
46    fn read(&mut self, _data: Data<TU>) {}
47}
48impl Size<TU> for Client {
49    fn len(&self) -> usize {
50        1234
51    }
52}
53
54#[derive(UID)]
55#[alias(name = TU, client = Client, traits = Write, Read, Size)]
56enum TUT {}
57```
58
59[UniqueIdentifier]: https://docs.rs/gmt_dos-clients/latest/gmt_dos_clients/interface/trait.UniqueIdentifier.html
60[Read]: https://docs.rs/gmt_dos-clients/latest/gmt_dos_clients/interface/trait.Read.html
61[Write]: https://docs.rs/gmt_dos-clients/latest/gmt_dos_clients/interface/trait.Write.html
62[Size]: https://docs.rs/gmt_dos-clients/latest/gmt_dos_clients/interface/trait.Size.html
63*/
64
65use proc_macro::TokenStream;
66use quote::quote;
67use syn::{parse_macro_input, DeriveInput};
68
69#[proc_macro_derive(UID, attributes(uid, alias))]
70pub fn derive(input: TokenStream) -> TokenStream {
71    let input = parse_macro_input!(input as DeriveInput);
72    Parser::new(&input)
73        .map_or_else(syn::Error::into_compile_error, |parser| {
74            if let Some(alias_attrs) = parser.alias_attrs {
75                let aliases: Vec<_> = alias_attrs
76                    .iter()
77                    .map(|alias| alias.expand(&input))
78                    .collect();
79                quote!(#(#aliases)*)
80            } else {
81                parser.uid_attrs.expand(&input)
82            }
83        })
84        .into()
85}
86
87mod alias;
88mod uid;
89
90/// Derive attributes parser
91///
92/// #[uid(...)]
93/// #[alias(...)]
94#[derive(Debug, Clone, Default)]
95struct Parser {
96    pub uid_attrs: uid::Attributes,
97    pub alias_attrs: Option<Vec<alias::Attributes>>,
98}
99
100impl Parser {
101    fn new(input: &DeriveInput) -> syn::Result<Parser> {
102        let mut parser: Parser = Default::default();
103        for attr in &input.attrs {
104            if attr.path().is_ident("uid") {
105                parser.uid_attrs = attr.parse_args()?;
106            }
107            if attr.path().is_ident("alias") {
108                parser
109                    .alias_attrs
110                    .get_or_insert(vec![])
111                    .push(attr.parse_args()?);
112            }
113        }
114        parser
115            .alias_attrs
116            .iter_mut()
117            .flatten()
118            .skip(1)
119            .for_each(|alias| {
120                alias.skip_uid = true;
121            });
122        Ok(parser)
123    }
124}
125
126type Expanded = proc_macro2::TokenStream;
127
128trait Expand {
129    fn expand(&self, input: &DeriveInput) -> Expanded;
130}