zenoh_macros/
lib.rs

1//
2// Copyright (c) 2023 ZettaScale Technology
3//
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7// which is available at https://www.apache.org/licenses/LICENSE-2.0.
8//
9// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10//
11// Contributors:
12//   ZettaScale Zenoh Team, <zenoh@zettascale.tech>
13//
14
15//! ⚠️ WARNING ⚠️
16//!
17//! This crate is intended for Zenoh's internal use.
18//!
19//! [Click here for Zenoh's documentation](https://docs.rs/zenoh/latest/zenoh)
20use proc_macro::TokenStream;
21use quote::{quote, ToTokens};
22use syn::{parse_macro_input, parse_quote, Attribute, Error, Item, ItemImpl, LitStr, TraitItem};
23use zenoh_keyexpr::{
24    format::{
25        macro_support::{self, SegmentBuilder},
26        KeFormat,
27    },
28    key_expr::keyexpr,
29};
30
31const RUSTC_VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/version.rs"));
32
33#[proc_macro]
34pub fn rustc_version_release(_tokens: TokenStream) -> TokenStream {
35    let release = RUSTC_VERSION
36        .split('\n')
37        .filter_map(|l| {
38            let line = l.trim();
39            if line.is_empty() {
40                None
41            } else {
42                Some(line)
43            }
44        })
45        .find_map(|l| l.strip_prefix("release: "))
46        .unwrap();
47    let commit = RUSTC_VERSION
48        .split('\n')
49        .filter_map(|l| {
50            let line = l.trim();
51            if line.is_empty() {
52                None
53            } else {
54                Some(line)
55            }
56        })
57        .find_map(|l| l.strip_prefix("commit-hash: "))
58        .unwrap();
59    (quote! {(#release, #commit)}).into()
60}
61
62/// An enumeration of items which can be annotated with `#[zenoh_macros::unstable_doc]`, #[zenoh_macros::unstable]`, `#[zenoh_macros::internal]`
63enum AnnotableItem {
64    /// Wrapper around [`syn::Item`].
65    Item(Item),
66    /// Wrapper around [`syn::TraitItem`].
67    TraitItem(TraitItem),
68}
69
70macro_rules! parse_annotable_item {
71    ($tokens:ident) => {{
72        let item: Item = parse_macro_input!($tokens as Item);
73
74        if matches!(item, Item::Verbatim(_)) {
75            let tokens = TokenStream::from(item.to_token_stream());
76            let trait_item: TraitItem = parse_macro_input!(tokens as TraitItem);
77
78            if matches!(trait_item, TraitItem::Verbatim(_)) {
79                Err(Error::new_spanned(
80                    trait_item,
81                    "the `unstable` proc-macro attribute only supports items and trait items",
82                ))
83            } else {
84                Ok(AnnotableItem::TraitItem(trait_item))
85            }
86        } else {
87            Ok(AnnotableItem::Item(item))
88        }
89    }};
90}
91
92impl AnnotableItem {
93    /// Mutably borrows the attribute list of this item.
94    fn attributes_mut(&mut self) -> Result<&mut Vec<Attribute>, Error> {
95        match self {
96            AnnotableItem::Item(item) => match item {
97                Item::Const(item) => Ok(&mut item.attrs),
98                Item::Enum(item) => Ok(&mut item.attrs),
99                Item::ExternCrate(item) => Ok(&mut item.attrs),
100                Item::Fn(item) => Ok(&mut item.attrs),
101                Item::ForeignMod(item) => Ok(&mut item.attrs),
102                Item::Impl(item) => Ok(&mut item.attrs),
103                Item::Macro(item) => Ok(&mut item.attrs),
104                Item::Mod(item) => Ok(&mut item.attrs),
105                Item::Static(item) => Ok(&mut item.attrs),
106                Item::Struct(item) => Ok(&mut item.attrs),
107                Item::Trait(item) => Ok(&mut item.attrs),
108                Item::TraitAlias(item) => Ok(&mut item.attrs),
109                Item::Type(item) => Ok(&mut item.attrs),
110                Item::Union(item) => Ok(&mut item.attrs),
111                Item::Use(item) => Ok(&mut item.attrs),
112                other => Err(Error::new_spanned(
113                    other,
114                    "item is not supported by the `unstable` or `internal` proc-macro attribute",
115                )),
116            },
117            AnnotableItem::TraitItem(trait_item) => match trait_item {
118                TraitItem::Const(trait_item) => Ok(&mut trait_item.attrs),
119                TraitItem::Fn(trait_item) => Ok(&mut trait_item.attrs),
120                TraitItem::Type(trait_item) => Ok(&mut trait_item.attrs),
121                TraitItem::Macro(trait_item) => Ok(&mut trait_item.attrs),
122                other => Err(Error::new_spanned(
123                    other,
124                    "item is not supported by the `unstable` or `internal` proc-macro attribute",
125                )),
126            },
127        }
128    }
129
130    /// Converts this item to a `proc_macro2::TokenStream`.
131    fn to_token_stream(&self) -> proc_macro2::TokenStream {
132        match self {
133            AnnotableItem::Item(item) => item.to_token_stream(),
134            AnnotableItem::TraitItem(trait_item) => trait_item.to_token_stream(),
135        }
136    }
137}
138
139#[proc_macro_attribute]
140/// Adds only piece of documentation about the item being unstable but no unstable attribute itself.
141/// This is useful when the whole crate is supposed to be used in unstable mode only, it makes sense
142/// to mention it in dcoumentation for the crate items, but not to add `#[cfg(feature = "unstable")]` to every item.
143pub fn unstable_doc(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
144    let mut item = match parse_annotable_item!(tokens) {
145        Ok(item) => item,
146        Err(err) => return err.into_compile_error().into(),
147    };
148
149    let attrs = match item.attributes_mut() {
150        Ok(attrs) => attrs,
151        Err(err) => return err.into_compile_error().into(),
152    };
153
154    if attrs.iter().any(is_doc_attribute) {
155        let mut pushed = false;
156        let oldattrs = std::mem::take(attrs);
157        for attr in oldattrs {
158            if is_doc_attribute(&attr) && !pushed {
159                attrs.push(attr);
160                // See: https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html#adding-a-warning-block
161                let message = "<div class=\"warning\">This API has been marked as <strong>unstable</strong>: it works as advertised, but it may be changed in a future release.</div>";
162                let note: Attribute = parse_quote!(#[doc = #message]);
163                attrs.push(note);
164                pushed = true;
165            } else {
166                attrs.push(attr);
167            }
168        }
169    }
170
171    TokenStream::from(item.to_token_stream())
172}
173
174#[proc_macro_attribute]
175/// Adds a `#[cfg(feature = "unstable")]` attribute to the item and appends piece of documentation about the item being unstable.
176pub fn unstable(attr: TokenStream, tokens: TokenStream) -> TokenStream {
177    let tokens = unstable_doc(attr, tokens);
178    let mut item = match parse_annotable_item!(tokens) {
179        Ok(item) => item,
180        Err(err) => return err.into_compile_error().into(),
181    };
182
183    let attrs = match item.attributes_mut() {
184        Ok(attrs) => attrs,
185        Err(err) => return err.into_compile_error().into(),
186    };
187
188    let feature_gate: Attribute = parse_quote!(#[cfg(feature = "unstable")]);
189    attrs.push(feature_gate);
190
191    TokenStream::from(item.to_token_stream())
192}
193
194// FIXME(fuzzypixelz): refactor `unstable` macro to accept arguments
195#[proc_macro_attribute]
196pub fn internal_config(args: TokenStream, tokens: TokenStream) -> TokenStream {
197    let tokens = unstable_doc(args, tokens);
198    let mut item = match parse_annotable_item!(tokens) {
199        Ok(item) => item,
200        Err(err) => return err.into_compile_error().into(),
201    };
202
203    let attrs = match item.attributes_mut() {
204        Ok(attrs) => attrs,
205        Err(err) => return err.into_compile_error().into(),
206    };
207
208    let feature_gate: Attribute = parse_quote!(#[cfg(feature = "internal_config")]);
209    let hide_doc: Attribute = parse_quote!(#[doc(hidden)]);
210    attrs.push(feature_gate);
211    attrs.push(hide_doc);
212
213    TokenStream::from(item.to_token_stream())
214}
215
216#[proc_macro_attribute]
217/// Adds a `#[cfg(feature = "internal")]` and `#[doc(hidden)]` attributes to the item.
218pub fn internal(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
219    let mut item = match parse_annotable_item!(tokens) {
220        Ok(item) => item,
221        Err(err) => return err.into_compile_error().into(),
222    };
223
224    let attrs = match item.attributes_mut() {
225        Ok(attrs) => attrs,
226        Err(err) => return err.into_compile_error().into(),
227    };
228
229    let feature_gate: Attribute = parse_quote!(#[cfg(feature = "internal")]);
230    let hide_doc: Attribute = parse_quote!(#[doc(hidden)]);
231    attrs.push(feature_gate);
232    attrs.push(hide_doc);
233
234    TokenStream::from(item.to_token_stream())
235}
236
237/// Returns `true` if the attribute is a `#[doc = "..."]` attribute.
238fn is_doc_attribute(attr: &Attribute) -> bool {
239    attr.path()
240        .get_ident()
241        .is_some_and(|ident| &ident.to_string() == "doc")
242}
243
244fn keformat_support(source: &str) -> proc_macro2::TokenStream {
245    let format = match KeFormat::new(&source) {
246        Ok(format) => format,
247        Err(e) => panic!("{}", e),
248    };
249    let specs = unsafe { macro_support::specs(&format) };
250    let len = specs.len();
251    let setters = specs.iter().map(|spec| {
252        let id = &source[spec.spec_start..(spec.spec_start + spec.id_end as usize)];
253        let set_id = quote::format_ident!("{}", id);
254        quote! {
255            pub fn #set_id <S: ::core::fmt::Display>(&mut self, value: S) -> Result<&mut Self, ::zenoh::key_expr::format::FormatSetError> {
256                match self.0.set(#id, value) {
257                    Ok(_) => Ok(self),
258                    Err(e) => Err(e)
259                }
260            }
261        }
262    });
263    let getters = specs.iter().map(|spec| {
264        let source = &source[spec.spec_start..spec.spec_end];
265        let id = &source[..(spec.id_end as usize)];
266        let get_id = quote::format_ident!("{}", id);
267        let pattern = unsafe {
268            keyexpr::from_str_unchecked(if spec.pattern_end != u16::MAX {
269                &source[(spec.id_end as usize + 1)..(spec.spec_start + spec.pattern_end as usize)]
270            } else {
271                &source[(spec.id_end as usize + 1)..]
272            })
273        };
274        let doc = format!("Get the parsed value for `{id}`.\n\nThis value is guaranteed to be a valid key expression intersecting with `{pattern}`");
275        if pattern.as_bytes() == b"**" {
276            quote! {
277                #[doc = #doc]
278                /// Since the pattern is `**`, this may return `None` if the pattern didn't consume any chunks.
279                pub fn #get_id (&self) -> Option<& ::zenoh::key_expr::keyexpr> {
280                    unsafe {
281                        let s =self._0.get(#id).unwrap_unchecked();
282                        (!s.is_empty()).then(|| ::zenoh::key_expr::keyexpr::from_str_unchecked(s))
283                    }
284                }
285            }
286        } else {
287            quote! {
288                #[doc = #doc]
289                pub fn #get_id (&self) -> &::zenoh::key_expr::keyexpr {
290                    unsafe {::zenoh::key_expr::keyexpr::from_str_unchecked(self._0.get(#id).unwrap_unchecked())}
291                }
292            }
293        }
294    });
295    let segments = specs.iter().map(|spec| {
296        let SegmentBuilder {
297            segment_start,
298            prefix_end,
299            spec_start,
300            id_end,
301            pattern_end,
302            spec_end,
303            segment_end,
304        } = spec;
305        quote! {
306            ::zenoh::key_expr::format::macro_support::SegmentBuilder {
307                segment_start: #segment_start,
308                prefix_end: #prefix_end,
309                spec_start: #spec_start,
310                id_end: #id_end,
311                pattern_end: #pattern_end,
312                spec_end: #spec_end,
313                segment_end: #segment_end,
314            },
315        }
316    });
317
318    let format_doc = format!("The `{source}` format, as a zero-sized-type.");
319    let formatter_doc = format!("And instance of a formatter for `{source}`.");
320
321    quote! {
322            use ::zenoh::Result as ZResult;
323            const FORMAT_INNER: ::zenoh::key_expr::format::KeFormat<'static, [::zenoh::key_expr::format::Segment<'static>; #len]> = unsafe {
324                ::zenoh::key_expr::format::macro_support::const_new(#source, [#(#segments)*])
325            };
326            #[doc = #format_doc]
327            #[derive(Copy, Clone, Hash)]
328            pub struct Format;
329
330            #[doc = #formatter_doc]
331            #[derive(Clone)]
332            pub struct Formatter(::zenoh::key_expr::format::KeFormatter<'static, [::zenoh::key_expr::format::Segment<'static>; #len]>);
333            impl ::core::fmt::Debug for Format {
334                fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
335                    ::core::fmt::Debug::fmt(&FORMAT_INNER, f)
336                }
337            }
338            impl ::core::fmt::Debug for Formatter {
339                fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
340                    ::core::fmt::Debug::fmt(&self.0, f)
341                }
342            }
343            impl ::core::fmt::Display for Format {
344                fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
345                    ::core::fmt::Display::fmt(&FORMAT_INNER, f)
346                }
347            }
348            impl ::core::ops::Deref for Format {
349                type Target = ::zenoh::key_expr::format::KeFormat<'static, [::zenoh::key_expr::format::Segment<'static>; #len]>;
350                fn deref(&self) -> &Self::Target {&FORMAT_INNER}
351            }
352            impl ::core::ops::Deref for Formatter {
353                type Target = ::zenoh::key_expr::format::KeFormatter<'static, [::zenoh::key_expr::format::Segment<'static>; #len]>;
354                fn deref(&self) -> &Self::Target {&self.0}
355            }
356            impl ::core::ops::DerefMut for Formatter {
357                fn deref_mut(&mut self) -> &mut Self::Target {&mut self.0}
358            }
359            impl Formatter {
360                #(#setters)*
361            }
362            pub struct Parsed<'s>{_0: ::zenoh::key_expr::format::Parsed<'s, [::zenoh::key_expr::format::Segment<'s>; #len]>}
363            impl<'s> ::core::ops::Deref for Parsed<'s> {
364                type Target = ::zenoh::key_expr::format::Parsed<'s, [::zenoh::key_expr::format::Segment<'s>; #len]>;
365                fn deref(&self) -> &Self::Target {&self._0}
366            }
367            impl Parsed<'_> {
368                #(#getters)*
369            }
370            impl Format {
371                pub fn formatter() -> Formatter {
372                    Formatter(Format.formatter())
373                }
374                pub fn parse<'s>(target: &'s ::zenoh::key_expr::keyexpr) -> ZResult<Parsed<'s>> {
375                    Ok(Parsed{_0: Format.parse(target)?})
376                }
377                pub fn into_inner(self) -> ::zenoh::key_expr::format::KeFormat<'static, [::zenoh::key_expr::format::Segment<'static>; #len]> {
378                    FORMAT_INNER
379                }
380            }
381            pub fn formatter() -> Formatter {
382                Format::formatter()
383            }
384            pub fn parse<'s>(target: &'s ::zenoh::key_expr::keyexpr) -> ZResult<Parsed<'s>> {
385                Format::parse(target)
386            }
387    }
388}
389
390struct FormatDeclaration {
391    vis: syn::Visibility,
392    name: syn::Ident,
393    lit: syn::LitStr,
394}
395impl syn::parse::Parse for FormatDeclaration {
396    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
397        let vis = input.parse()?;
398        let name = input.parse()?;
399        let _: syn::Token!(:) = input.parse()?;
400        let lit = input.parse()?;
401        Ok(FormatDeclaration { vis, name, lit })
402    }
403}
404struct FormatDeclarations(syn::punctuated::Punctuated<FormatDeclaration, syn::Token!(,)>);
405impl syn::parse::Parse for FormatDeclarations {
406    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
407        Ok(Self(input.parse_terminated(
408            FormatDeclaration::parse,
409            syn::Token![,],
410        )?))
411    }
412}
413
414/// Create format modules from a format specification.
415///
416/// `kedefine!($($vis $ident: $lit),*)` will validate each `$lit` to be a valid KeFormat, and declare a module called `$ident` with `$vis` visibility at its call-site for each format.
417///  The modules contain the following elements:
418/// - `Format`, a zero-sized type that represents your format.
419/// - `formatter()`, a function that constructs a `Formatter` specialized for your format:
420///     - for every spec in your format, `Formatter` will have a method named after the spec's `id` that lets you set a value for that field of your format. These methods will return `Result<&mut Formatter, FormatError>`.
421/// - `parse(target: &keyexpr) -> ZResult<Parsed<'_>>` will parse the provided key expression according to your format. Just like `KeFormat::parse`, parsing is lazy: each field will match the smallest subsection of your `target` that is included in its pattern.
422///     - like `Formatter`, `Parsed` will have a method named after each spec's `id` that returns `&keyexpr`; except for specs whose pattern was `**`, these will return an `Option<&keyexpr>`, where `None` signifies that the pattern was matched by an empty list of chunks.
423#[proc_macro]
424pub fn kedefine(tokens: TokenStream) -> TokenStream {
425    let declarations: FormatDeclarations = syn::parse(tokens).unwrap();
426    let content = declarations.0.into_iter().map(|FormatDeclaration { vis, name, lit }|
427    {
428    let source = lit.value();
429    let docstring = format!(
430        r"The module associated with the `{source}` format, it contains:
431- `Format`, a zero-sized type that represents your format.
432- `formatter()`, a function that constructs a `Formatter` specialized for your format:
433    - for every spec in your format, `Formatter` will have a method named after the spec's `id` that lets you set a value for that field of your format. These methods will return `Result<&mut Formatter, FormatError>`.
434- `parse(target: &keyexpr) -> ZResult<Parsed<'_>>` will parse the provided key expression according to your format. Just like `KeFormat::parse`, parsing is lazy: each field will match the smallest subsection of your `target` that is included in its pattern.
435    - like `Formatter`, `Parsed` will have a method named after each spec's `id` that returns `&keyexpr`; except for specs whose pattern was `**`, these will return an `Option<&keyexpr>`, where `None` signifies that the pattern was matched by an empty list of chunks."
436    );
437    let support = keformat_support(&source);
438    quote! {
439        #[doc = #docstring]
440        #vis mod #name{
441            #support
442        }
443    }});
444    quote!(#(#content)*).into()
445}
446
447struct FormatUsage {
448    id: syn::Expr,
449    assigns: Vec<(syn::Expr, syn::Expr)>,
450}
451impl syn::parse::Parse for FormatUsage {
452    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
453        let id = input.parse()?;
454        let mut assigns = Vec::new();
455        if !input.is_empty() {
456            input.parse::<syn::Token!(,)>()?;
457        }
458        assigns.extend(
459            input
460                .parse_terminated(syn::Expr::parse, syn::Token![,])?
461                .into_iter()
462                .map(|a| match a {
463                    syn::Expr::Assign(a) => (*a.left, *a.right),
464                    a => (a.clone(), a),
465                }),
466        );
467        Ok(FormatUsage { id, assigns })
468    }
469}
470
471/// Write a set of values into a `Formatter`, stopping as soon as a value doesn't fit the specification for its field.
472/// Contrary to `keformat` doesn't build the Formatter into a Key Expression.
473///
474/// `kewrite!($formatter, $($ident [= $expr]),*)` will attempt to write `$expr` into their respective `$ident` fields for `$formatter`.
475/// `$formatter` must be an expression that dereferences to `&mut Formatter`.
476/// `$expr` must resolve to a value that implements `core::fmt::Display`.
477/// `$expr` defaults to `$ident` if omitted.
478///
479/// This macro always results in an expression that resolves to `Result<&mut Formatter, FormatSetError>`.
480#[proc_macro]
481pub fn kewrite(tokens: TokenStream) -> TokenStream {
482    let FormatUsage { id, assigns } = syn::parse(tokens).unwrap();
483    let mut sets = None;
484    for (l, r) in assigns.iter().rev() {
485        if let Some(set) = sets {
486            sets = Some(quote!(.#l(#r).and_then(|x| x #set)));
487        } else {
488            sets = Some(quote!(.#l(#r)));
489        }
490    }
491    quote!(#id #sets).into()
492}
493
494/// Write a set of values into a `Formatter` and then builds it into an `OwnedKeyExpr`, stopping as soon as a value doesn't fit the specification for its field.
495///
496/// `keformat!($formatter, $($ident [= $expr]),*)` will attempt to write `$expr` into their respective `$ident` fields for `$formatter`.
497/// `$formatter` must be an expression that dereferences to `&mut Formatter`.
498/// `$expr` must resolve to a value that implements `core::fmt::Display`.
499/// `$expr` defaults to `$ident` if omitted.
500///
501/// This macro always results in an expression that resolves to `ZResult<OwnedKeyExpr>`, and leaves `$formatter` in its written state.
502#[proc_macro]
503pub fn keformat(tokens: TokenStream) -> TokenStream {
504    let formatted: proc_macro2::TokenStream = kewrite(tokens).into();
505    quote!(match #formatted {
506        Ok(ok) => ok.build(),
507        Err(e) => Err(e.into()),
508    })
509    .into()
510}
511
512/// Equivalent to [`keyexpr::new`](zenoh_keyexpr::keyexpr::new), but the check is run at compile-time and will throw a compile error in case of failure.
513#[proc_macro]
514pub fn ke(tokens: TokenStream) -> TokenStream {
515    let value: LitStr = syn::parse(tokens).unwrap();
516    let ke = value.value();
517    match zenoh_keyexpr::keyexpr::new(&ke) {
518        Ok(_) => quote!(unsafe { zenoh::key_expr::keyexpr::from_str_unchecked(#ke)}).into(),
519        Err(e) => panic!("{}", e),
520    }
521}
522
523/// Equivalent to [`nonwild_keyexpr::new`](zenoh_keyexpr::nonwild_keyexpr::new), but the check is run at compile-time and will throw a compile error in case of failure.
524#[proc_macro]
525pub fn nonwild_ke(tokens: TokenStream) -> TokenStream {
526    let value: LitStr = syn::parse(tokens).unwrap();
527    let ke = value.value();
528    match zenoh_keyexpr::nonwild_keyexpr::new(&ke) {
529        Ok(_) => quote!(unsafe { zenoh::key_expr::nonwild_keyexpr::from_str_unchecked(#ke)}).into(),
530        Err(e) => panic!("{}", e),
531    }
532}
533
534mod zenoh_runtime_derive;
535use syn::DeriveInput;
536use zenoh_runtime_derive::{derive_generic_runtime_param, derive_register_param};
537
538/// Make the underlying struct `Param` be generic over any `T` satisfying a generated `trait DefaultParam { fn param() -> Param; }`
539/// ```rust,ignore
540/// #[derive(GenericRuntimeParam)]
541/// struct Param {
542///    ...
543/// }
544/// ```
545#[proc_macro_derive(GenericRuntimeParam)]
546pub fn generic_runtime_param(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
547    let input: DeriveInput = syn::parse_macro_input!(input);
548    derive_generic_runtime_param(input)
549        .unwrap_or_else(syn::Error::into_compile_error)
550        .into()
551}
552
553/// Register the input `Enum` with the struct `Param` specified in the param attribute
554/// ```rust,ignore
555/// #[derive(RegisterParam)]
556/// #[param(Param)]
557/// enum Enum {
558///    ...
559/// }
560/// ```
561#[proc_macro_derive(RegisterParam, attributes(alias, param))]
562pub fn register_param(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
563    let input: DeriveInput = syn::parse_macro_input!(input);
564    derive_register_param(input)
565        .unwrap_or_else(syn::Error::into_compile_error)
566        .into()
567}
568
569/// Macro `#[internal_trait]` should precede
570/// `impl Trait for Struct { ... }`
571///
572/// This macro wraps the implementations of "internal" tratis.
573///
574/// These traits are used to group set of functions which should be implemented
575/// together and with the same portotyoe. E.g. `QoSBuilderTrait` provides set of
576/// setters (`congestion_control`, `priority`, `express`) and we should not
577/// forget to implement all these setters for each entity which supports
578/// QoS functionality.
579///
580/// The traits mechanism is a good way to group functions. But additional traits
581/// adds extra burden to end user who have to import it every time.
582///
583/// The macro `internal_trait` solves this problem by adding
584/// methods with same names as in trait to structure implementation itself,
585/// making them available to user without additional trait import.
586///
587#[proc_macro_attribute]
588pub fn internal_trait(_attr: TokenStream, item: TokenStream) -> TokenStream {
589    let input = parse_macro_input!(item as ItemImpl);
590    let trait_path = &input.trait_.as_ref().unwrap().1;
591    let struct_path = &input.self_ty;
592    let generics = &input.generics;
593    // let struct_lifetime = get_type_path_lifetime(struct_path);
594
595    let mut struct_methods = quote! {};
596    for item_fn in input.items.iter() {
597        if let syn::ImplItem::Fn(method) = item_fn {
598            let method_name = &method.sig.ident;
599            let method_generic_params = &method.sig.generics.params;
600            let method_generic_params = if method_generic_params.is_empty() {
601                quote! {}
602            } else {
603                quote! {<#method_generic_params>}
604            };
605            let method_args = &method.sig.inputs;
606            let method_output = &method.sig.output;
607            let where_clause = &method.sig.generics.where_clause;
608            let mut method_call_args = quote! {};
609            for arg in method_args.iter() {
610                match arg {
611                    syn::FnArg::Receiver(_) => {
612                        method_call_args.extend(quote! { self, });
613                    }
614                    syn::FnArg::Typed(pat_type) => {
615                        let pat = &pat_type.pat;
616                        method_call_args.extend(quote! { #pat, });
617                    }
618                }
619            }
620            let mut attributes = quote! {};
621            for attr in &method.attrs {
622                attributes.extend(quote! {
623                    #attr
624                });
625            }
626            // call corresponding trait method from struct method
627            struct_methods.extend(quote! {
628                #attributes
629                pub fn #method_name #method_generic_params (#method_args) #method_output #where_clause {
630                    <#struct_path as #trait_path>::#method_name(#method_call_args)
631                }
632            });
633        }
634    }
635    let struct_methods_output = quote! {
636        impl #generics #struct_path {
637            #struct_methods
638        }
639    };
640    (quote! {
641        #input
642        #struct_methods_output
643    })
644    .into()
645}