Skip to main content

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::{
23    parse_macro_input, parse_quote, spanned::Spanned, Attribute, Error, Item, ItemImpl, LitStr,
24    TraitItem,
25};
26use zenoh_keyexpr::{
27    format::{
28        macro_support::{self, SegmentBuilder},
29        KeFormat,
30    },
31    key_expr::keyexpr,
32};
33
34const RUSTC_VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/version.rs"));
35
36#[proc_macro]
37pub fn rustc_version_release(_tokens: TokenStream) -> TokenStream {
38    let release = RUSTC_VERSION
39        .split('\n')
40        .filter_map(|l| {
41            let line = l.trim();
42            if line.is_empty() {
43                None
44            } else {
45                Some(line)
46            }
47        })
48        .find_map(|l| l.strip_prefix("release: "))
49        .unwrap();
50    let commit = RUSTC_VERSION
51        .split('\n')
52        .filter_map(|l| {
53            let line = l.trim();
54            if line.is_empty() {
55                None
56            } else {
57                Some(line)
58            }
59        })
60        .find_map(|l| l.strip_prefix("commit-hash: "))
61        .unwrap();
62    (quote! {(#release, #commit)}).into()
63}
64
65/// An enumeration of items which can be annotated with `#[zenoh_macros::unstable_doc]`, #[zenoh_macros::unstable]`, `#[zenoh_macros::internal]`
66#[allow(clippy::large_enum_variant)]
67enum AnnotableItem {
68    /// Wrapper around [`syn::Item`].
69    Item(Item),
70    /// Wrapper around [`syn::TraitItem`].
71    TraitItem(TraitItem),
72}
73
74macro_rules! parse_annotable_item {
75    ($tokens:ident) => {{
76        let item: Item = parse_macro_input!($tokens as Item);
77
78        if matches!(item, Item::Verbatim(_)) {
79            let tokens = TokenStream::from(item.to_token_stream());
80            let trait_item: TraitItem = parse_macro_input!(tokens as TraitItem);
81
82            if matches!(trait_item, TraitItem::Verbatim(_)) {
83                Err(Error::new_spanned(
84                    trait_item,
85                    "the `unstable` proc-macro attribute only supports items and trait items",
86                ))
87            } else {
88                Ok(AnnotableItem::TraitItem(trait_item))
89            }
90        } else {
91            Ok(AnnotableItem::Item(item))
92        }
93    }};
94}
95
96impl AnnotableItem {
97    /// Mutably borrows the attribute list of this item.
98    fn attributes_mut(&mut self) -> Result<&mut Vec<Attribute>, Error> {
99        match self {
100            AnnotableItem::Item(item) => match item {
101                Item::Const(item) => Ok(&mut item.attrs),
102                Item::Enum(item) => Ok(&mut item.attrs),
103                Item::ExternCrate(item) => Ok(&mut item.attrs),
104                Item::Fn(item) => Ok(&mut item.attrs),
105                Item::ForeignMod(item) => Ok(&mut item.attrs),
106                Item::Impl(item) => Ok(&mut item.attrs),
107                Item::Macro(item) => Ok(&mut item.attrs),
108                Item::Mod(item) => Ok(&mut item.attrs),
109                Item::Static(item) => Ok(&mut item.attrs),
110                Item::Struct(item) => Ok(&mut item.attrs),
111                Item::Trait(item) => Ok(&mut item.attrs),
112                Item::TraitAlias(item) => Ok(&mut item.attrs),
113                Item::Type(item) => Ok(&mut item.attrs),
114                Item::Union(item) => Ok(&mut item.attrs),
115                Item::Use(item) => Ok(&mut item.attrs),
116                other => Err(Error::new_spanned(
117                    other,
118                    "item is not supported by the `unstable` or `internal` proc-macro attribute",
119                )),
120            },
121            AnnotableItem::TraitItem(trait_item) => match trait_item {
122                TraitItem::Const(trait_item) => Ok(&mut trait_item.attrs),
123                TraitItem::Fn(trait_item) => Ok(&mut trait_item.attrs),
124                TraitItem::Type(trait_item) => Ok(&mut trait_item.attrs),
125                TraitItem::Macro(trait_item) => Ok(&mut trait_item.attrs),
126                other => Err(Error::new_spanned(
127                    other,
128                    "item is not supported by the `unstable` or `internal` proc-macro attribute",
129                )),
130            },
131        }
132    }
133
134    /// Converts this item to a `proc_macro2::TokenStream`.
135    fn to_token_stream(&self) -> proc_macro2::TokenStream {
136        match self {
137            AnnotableItem::Item(item) => item.to_token_stream(),
138            AnnotableItem::TraitItem(trait_item) => trait_item.to_token_stream(),
139        }
140    }
141}
142
143/// Adds unstable warning to documentation. Returns Result for proper error handling.
144fn add_unstable_warning(item: &mut AnnotableItem) -> Result<(), Error> {
145    let attrs = item.attributes_mut()?;
146
147    let mut old_attrs = std::mem::take(attrs).into_iter();
148
149    // First loop: copy attrs until first doc attr, add warning after it
150    for attr in old_attrs.by_ref() {
151        attrs.push(attr);
152        if get_doc_str(attrs.last().unwrap()).is_some() {
153            // See: https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html#adding-a-warning-block
154            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>";
155            let note: Attribute = parse_quote!(#[doc = #message]);
156            attrs.push(note);
157            break;
158        }
159    }
160
161    // Second loop: copy attrs until second doc attr, validate that it's blank
162    for attr in old_attrs.by_ref() {
163        if let Some(lit_str) = get_doc_str(&attr) {
164            if !lit_str.value().trim().is_empty() {
165                return Err(Error::new_spanned(
166                    attr,
167                    "documentation for `#[unstable]` items must have a blank doc line after the brief description. \
168                     This blank line is critical for proper formatting: it will separate the unstable warning from the content below and ensures that \
169                     markdown links (like [`Type`](path::to::Type)) are correctly processed by rustdoc. \
170                     Add an empty `///` line after your brief description and before detailed explanations.",
171                ));
172            }
173            attrs.push(attr);
174            break;
175        }
176        attrs.push(attr);
177    }
178
179    // Third loop: copy remaining attrs
180    attrs.extend(old_attrs);
181
182    Ok(())
183}
184
185#[proc_macro_attribute]
186/// Adds only piece of documentation about the item being unstable but no unstable attribute itself.
187/// This is useful when the whole crate is supposed to be used in unstable mode only, it makes sense
188/// to mention it in documentation for the crate items, but not to add `#[cfg(feature = "unstable")]` to every item.
189pub fn unstable_doc(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
190    let mut item = match parse_annotable_item!(tokens) {
191        Ok(item) => item,
192        Err(err) => return err.into_compile_error().into(),
193    };
194
195    if let Err(err) = add_unstable_warning(&mut item) {
196        return err.into_compile_error().into();
197    }
198
199    TokenStream::from(item.to_token_stream())
200}
201
202#[proc_macro_attribute]
203/// Adds a `#[cfg(feature = "unstable")]` attribute to the item and appends piece of documentation about the item being unstable.
204pub fn unstable(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
205    let mut item = match parse_annotable_item!(tokens) {
206        Ok(item) => item,
207        Err(err) => return err.into_compile_error().into(),
208    };
209
210    if let Err(err) = add_unstable_warning(&mut item) {
211        return err.into_compile_error().into();
212    }
213
214    let attrs = match item.attributes_mut() {
215        Ok(attrs) => attrs,
216        Err(err) => return err.into_compile_error().into(),
217    };
218
219    let feature_gate: Attribute = parse_quote!(#[cfg(feature = "unstable")]);
220    attrs.push(feature_gate);
221
222    TokenStream::from(item.to_token_stream())
223}
224
225#[proc_macro_attribute]
226/// Adds a `#[cfg(feature = "internal")]` and `#[doc(hidden)]` attributes to the item.
227pub fn internal(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
228    let mut item = match parse_annotable_item!(tokens) {
229        Ok(item) => item,
230        Err(err) => return err.into_compile_error().into(),
231    };
232
233    let attrs = match item.attributes_mut() {
234        Ok(attrs) => attrs,
235        Err(err) => return err.into_compile_error().into(),
236    };
237
238    let feature_gate: Attribute = parse_quote!(#[cfg(feature = "internal")]);
239    let hide_doc: Attribute = parse_quote!(#[doc(hidden)]);
240    attrs.push(feature_gate);
241    attrs.push(hide_doc);
242
243    TokenStream::from(item.to_token_stream())
244}
245
246/// Extracts the string literal from a `#[doc = "..."]` attribute.
247fn get_doc_str(attr: &Attribute) -> Option<&LitStr> {
248    if attr
249        .path()
250        .get_ident()
251        .is_some_and(|ident| &ident.to_string() == "doc")
252    {
253        if let syn::Meta::NameValue(nv) = &attr.meta {
254            if let syn::Expr::Lit(syn::ExprLit {
255                lit: syn::Lit::Str(lit_str),
256                ..
257            }) = &nv.value
258            {
259                return Some(lit_str);
260            }
261        }
262    }
263    None
264}
265
266fn keformat_support(source: &str) -> proc_macro2::TokenStream {
267    let format = match KeFormat::new(&source) {
268        Ok(format) => format,
269        Err(e) => panic!("{}", e),
270    };
271    let specs = unsafe { macro_support::specs(&format) };
272    let len = specs.len();
273    let setters = specs.iter().map(|spec| {
274        let id = &source[spec.spec_start..(spec.spec_start + spec.id_end as usize)];
275        let set_id = quote::format_ident!("{}", id);
276        quote! {
277            pub fn #set_id <S: ::core::fmt::Display>(&mut self, value: S) -> Result<&mut Self, ::zenoh::key_expr::format::FormatSetError> {
278                match self.0.set(#id, value) {
279                    Ok(_) => Ok(self),
280                    Err(e) => Err(e)
281                }
282            }
283        }
284    });
285    let getters = specs.iter().map(|spec| {
286        let source = &source[spec.spec_start..spec.spec_end];
287        let id = &source[..(spec.id_end as usize)];
288        let get_id = quote::format_ident!("{}", id);
289        let pattern = unsafe {
290            keyexpr::from_str_unchecked(if spec.pattern_end != u16::MAX {
291                &source[(spec.id_end as usize + 1)..(spec.spec_start + spec.pattern_end as usize)]
292            } else {
293                &source[(spec.id_end as usize + 1)..]
294            })
295        };
296        let doc = format!("Get the parsed value for `{id}`.\n\nThis value is guaranteed to be a valid key expression intersecting with `{pattern}`");
297        if pattern.as_bytes() == b"**" {
298            quote! {
299                #[doc = #doc]
300                /// Since the pattern is `**`, this may return `None` if the pattern didn't consume any chunks.
301                pub fn #get_id (&self) -> Option<& ::zenoh::key_expr::keyexpr> {
302                    unsafe {
303                        let s =self._0.get(#id).unwrap_unchecked();
304                        (!s.is_empty()).then(|| ::zenoh::key_expr::keyexpr::from_str_unchecked(s))
305                    }
306                }
307            }
308        } else {
309            quote! {
310                #[doc = #doc]
311                pub fn #get_id (&self) -> &::zenoh::key_expr::keyexpr {
312                    unsafe {::zenoh::key_expr::keyexpr::from_str_unchecked(self._0.get(#id).unwrap_unchecked())}
313                }
314            }
315        }
316    });
317    let segments = specs.iter().map(|spec| {
318        let SegmentBuilder {
319            segment_start,
320            prefix_end,
321            spec_start,
322            id_end,
323            pattern_end,
324            spec_end,
325            segment_end,
326        } = spec;
327        quote! {
328            ::zenoh::key_expr::format::macro_support::SegmentBuilder {
329                segment_start: #segment_start,
330                prefix_end: #prefix_end,
331                spec_start: #spec_start,
332                id_end: #id_end,
333                pattern_end: #pattern_end,
334                spec_end: #spec_end,
335                segment_end: #segment_end,
336            },
337        }
338    });
339
340    let format_doc = format!("The `{source}` format, as a zero-sized-type.");
341    let formatter_doc = format!("And instance of a formatter for `{source}`.");
342
343    quote! {
344            use ::zenoh::Result as ZResult;
345            const FORMAT_INNER: ::zenoh::key_expr::format::KeFormat<'static, [::zenoh::key_expr::format::Segment<'static>; #len]> = unsafe {
346                ::zenoh::key_expr::format::macro_support::const_new(#source, [#(#segments)*])
347            };
348            #[doc = #format_doc]
349            #[derive(Copy, Clone, Hash)]
350            pub struct Format;
351
352            #[doc = #formatter_doc]
353            #[derive(Clone)]
354            pub struct Formatter(::zenoh::key_expr::format::KeFormatter<'static, [::zenoh::key_expr::format::Segment<'static>; #len]>);
355            impl ::core::fmt::Debug for Format {
356                fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
357                    ::core::fmt::Debug::fmt(&FORMAT_INNER, f)
358                }
359            }
360            impl ::core::fmt::Debug for Formatter {
361                fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
362                    ::core::fmt::Debug::fmt(&self.0, f)
363                }
364            }
365            impl ::core::fmt::Display for Format {
366                fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
367                    ::core::fmt::Display::fmt(&FORMAT_INNER, f)
368                }
369            }
370            impl ::core::ops::Deref for Format {
371                type Target = ::zenoh::key_expr::format::KeFormat<'static, [::zenoh::key_expr::format::Segment<'static>; #len]>;
372                fn deref(&self) -> &Self::Target {&FORMAT_INNER}
373            }
374            impl ::core::ops::Deref for Formatter {
375                type Target = ::zenoh::key_expr::format::KeFormatter<'static, [::zenoh::key_expr::format::Segment<'static>; #len]>;
376                fn deref(&self) -> &Self::Target {&self.0}
377            }
378            impl ::core::ops::DerefMut for Formatter {
379                fn deref_mut(&mut self) -> &mut Self::Target {&mut self.0}
380            }
381            impl Formatter {
382                #(#setters)*
383            }
384            pub struct Parsed<'s>{_0: ::zenoh::key_expr::format::Parsed<'s, [::zenoh::key_expr::format::Segment<'s>; #len]>}
385            impl<'s> ::core::ops::Deref for Parsed<'s> {
386                type Target = ::zenoh::key_expr::format::Parsed<'s, [::zenoh::key_expr::format::Segment<'s>; #len]>;
387                fn deref(&self) -> &Self::Target {&self._0}
388            }
389            impl Parsed<'_> {
390                #(#getters)*
391            }
392            impl Format {
393                pub fn formatter() -> Formatter {
394                    Formatter(Format.formatter())
395                }
396                pub fn parse<'s>(target: &'s ::zenoh::key_expr::keyexpr) -> ZResult<Parsed<'s>> {
397                    Ok(Parsed{_0: Format.parse(target)?})
398                }
399                pub fn into_inner(self) -> ::zenoh::key_expr::format::KeFormat<'static, [::zenoh::key_expr::format::Segment<'static>; #len]> {
400                    FORMAT_INNER
401                }
402            }
403            pub fn formatter() -> Formatter {
404                Format::formatter()
405            }
406            pub fn parse<'s>(target: &'s ::zenoh::key_expr::keyexpr) -> ZResult<Parsed<'s>> {
407                Format::parse(target)
408            }
409    }
410}
411
412struct FormatDeclaration {
413    vis: syn::Visibility,
414    name: syn::Ident,
415    lit: syn::LitStr,
416}
417impl syn::parse::Parse for FormatDeclaration {
418    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
419        let vis = input.parse()?;
420        let name = input.parse()?;
421        let _: syn::Token!(:) = input.parse()?;
422        let lit = input.parse()?;
423        Ok(FormatDeclaration { vis, name, lit })
424    }
425}
426struct FormatDeclarations(syn::punctuated::Punctuated<FormatDeclaration, syn::Token!(,)>);
427impl syn::parse::Parse for FormatDeclarations {
428    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
429        Ok(Self(input.parse_terminated(
430            FormatDeclaration::parse,
431            syn::Token![,],
432        )?))
433    }
434}
435
436/// Create format modules from a format specification.
437///
438/// `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.
439///  The modules contain the following elements:
440/// - `Format`, a zero-sized type that represents your format.
441/// - `formatter()`, a function that constructs a `Formatter` specialized for your format:
442///     - 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>`.
443/// - `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.
444///     - 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.
445#[proc_macro]
446pub fn kedefine(tokens: TokenStream) -> TokenStream {
447    let declarations: FormatDeclarations = syn::parse(tokens).unwrap();
448    let content = declarations.0.into_iter().map(|FormatDeclaration { vis, name, lit }|
449    {
450    let source = lit.value();
451    let docstring = format!(
452        r"The module associated with the `{source}` format, it contains:
453- `Format`, a zero-sized type that represents your format.
454- `formatter()`, a function that constructs a `Formatter` specialized for your format:
455    - 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>`.
456- `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.
457    - 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."
458    );
459    let support = keformat_support(&source);
460    quote! {
461        #[doc = #docstring]
462        #vis mod #name{
463            #support
464        }
465    }});
466    quote!(#(#content)*).into()
467}
468
469struct FormatUsage {
470    id: syn::Expr,
471    assigns: Vec<(syn::Expr, syn::Expr)>,
472}
473impl syn::parse::Parse for FormatUsage {
474    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
475        let id = input.parse()?;
476        let mut assigns = Vec::new();
477        if !input.is_empty() {
478            input.parse::<syn::Token!(,)>()?;
479        }
480        assigns.extend(
481            input
482                .parse_terminated(syn::Expr::parse, syn::Token![,])?
483                .into_iter()
484                .map(|a| match a {
485                    syn::Expr::Assign(a) => (*a.left, *a.right),
486                    a => (a.clone(), a),
487                }),
488        );
489        Ok(FormatUsage { id, assigns })
490    }
491}
492
493/// Write a set of values into a `Formatter`, stopping as soon as a value doesn't fit the specification for its field.
494/// Contrary to `keformat` doesn't build the Formatter into a Key Expression.
495///
496/// `kewrite!($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 `Result<&mut Formatter, FormatSetError>`.
502#[proc_macro]
503pub fn kewrite(tokens: TokenStream) -> TokenStream {
504    let FormatUsage { id, assigns } = syn::parse(tokens).unwrap();
505    let mut sets = None;
506    for (l, r) in assigns.iter().rev() {
507        if let Some(set) = sets {
508            sets = Some(quote!(.#l(#r).and_then(|x| x #set)));
509        } else {
510            sets = Some(quote!(.#l(#r)));
511        }
512    }
513    quote!(#id #sets).into()
514}
515
516/// 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.
517///
518/// `keformat!($formatter, $($ident [= $expr]),*)` will attempt to write `$expr` into their respective `$ident` fields for `$formatter`.
519/// `$formatter` must be an expression that dereferences to `&mut Formatter`.
520/// `$expr` must resolve to a value that implements `core::fmt::Display`.
521/// `$expr` defaults to `$ident` if omitted.
522///
523/// This macro always results in an expression that resolves to `ZResult<OwnedKeyExpr>`, and leaves `$formatter` in its written state.
524#[proc_macro]
525pub fn keformat(tokens: TokenStream) -> TokenStream {
526    let formatted: proc_macro2::TokenStream = kewrite(tokens).into();
527    quote!(match #formatted {
528        Ok(ok) => ok.build(),
529        Err(e) => Err(e.into()),
530    })
531    .into()
532}
533
534/// 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.
535#[proc_macro]
536pub fn ke(tokens: TokenStream) -> TokenStream {
537    let value: LitStr = syn::parse(tokens).unwrap();
538    let ke = value.value();
539    match zenoh_keyexpr::keyexpr::new(&ke) {
540        Ok(_) => quote!(unsafe { zenoh::key_expr::keyexpr::from_str_unchecked(#ke)}).into(),
541        Err(e) => panic!("{}", e),
542    }
543}
544
545/// 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.
546#[proc_macro]
547pub fn nonwild_ke(tokens: TokenStream) -> TokenStream {
548    let value: LitStr = syn::parse(tokens).unwrap();
549    let ke = value.value();
550    match zenoh_keyexpr::nonwild_keyexpr::new(&ke) {
551        Ok(_) => quote!(unsafe { zenoh::key_expr::nonwild_keyexpr::from_str_unchecked(#ke)}).into(),
552        Err(e) => panic!("{}", e),
553    }
554}
555
556mod zenoh_runtime_derive;
557use syn::DeriveInput;
558use zenoh_runtime_derive::{derive_generic_runtime_param, derive_register_param};
559
560/// Make the underlying struct `Param` be generic over any `T` satisfying a generated `trait DefaultParam { fn param() -> Param; }`
561/// ```rust,ignore
562/// #[derive(GenericRuntimeParam)]
563/// struct Param {
564///    ...
565/// }
566/// ```
567#[proc_macro_derive(GenericRuntimeParam)]
568pub fn generic_runtime_param(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
569    let input: DeriveInput = syn::parse_macro_input!(input);
570    derive_generic_runtime_param(input)
571        .unwrap_or_else(syn::Error::into_compile_error)
572        .into()
573}
574
575/// Register the input `Enum` with the struct `Param` specified in the param attribute
576/// ```rust,ignore
577/// #[derive(RegisterParam)]
578/// #[param(Param)]
579/// enum Enum {
580///    ...
581/// }
582/// ```
583#[proc_macro_derive(RegisterParam, attributes(alias, param))]
584pub fn register_param(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
585    let input: DeriveInput = syn::parse_macro_input!(input);
586    derive_register_param(input)
587        .unwrap_or_else(syn::Error::into_compile_error)
588        .into()
589}
590
591/// Macro `#[internal_trait]` should precede
592/// `impl Trait for Struct { ... }`
593///
594/// This macro wraps the implementations of "internal" tratis.
595///
596/// These traits are used to group set of functions which should be implemented
597/// together and with the same prototype. E.g. `QoSBuilderTrait` provides set of
598/// setters (`congestion_control`, `priority`, `express`) and we should not
599/// forget to implement all these setters for each entity which supports
600/// QoS functionality.
601///
602/// The traits mechanism is a good way to group functions. But additional traits
603/// adds extra burden to end user who have to import it every time.
604///
605/// The macro `internal_trait` solves this problem by adding
606/// methods with same names as in trait to structure implementation itself,
607/// making them available to user without additional trait import.
608///
609#[proc_macro_attribute]
610pub fn internal_trait(_attr: TokenStream, item: TokenStream) -> TokenStream {
611    let mut input = parse_macro_input!(item as ItemImpl);
612    let trait_path = &input.trait_.as_ref().unwrap().1;
613    let struct_path = &input.self_ty;
614    let generics = &input.generics;
615    // let struct_lifetime = get_type_path_lifetime(struct_path);
616
617    let mut struct_methods = quote! {};
618    for item_fn in input.items.iter_mut() {
619        if let syn::ImplItem::Fn(method) = item_fn {
620            let method_name = &method.sig.ident;
621            let method_generic_params = &method.sig.generics.params;
622            let method_generic_params = if method_generic_params.is_empty() {
623                quote! {}
624            } else {
625                quote! {<#method_generic_params>}
626            };
627            let method_args = &method.sig.inputs;
628            let method_output = &method.sig.output;
629            let where_clause = &method.sig.generics.where_clause;
630            let mut method_call_args = quote! {};
631            for arg in method_args.iter() {
632                match arg {
633                    syn::FnArg::Receiver(_) => {
634                        method_call_args.extend(quote! { self, });
635                    }
636                    syn::FnArg::Typed(pat_type) => {
637                        let pat = &pat_type.pat;
638                        method_call_args.extend(quote! { #pat, });
639                    }
640                }
641            }
642            let mut attributes = quote! {};
643            for attr in &method.attrs {
644                attributes.extend(quote! {
645                    #attr
646                });
647            }
648            method
649                .attrs
650                .retain(|attr| !attr.meta.path().is_ident("deprecated"));
651            // call corresponding trait method from struct method
652            struct_methods.extend(quote! {
653                #attributes
654                pub fn #method_name #method_generic_params (#method_args) #method_output #where_clause {
655                    <#struct_path as #trait_path>::#method_name(#method_call_args)
656                }
657            });
658        }
659    }
660    let struct_methods_output = quote! {
661        impl #generics #struct_path {
662            #struct_methods
663        }
664    };
665    (quote! {
666        #input
667        #struct_methods_output
668    })
669    .into()
670}
671
672#[proc_macro_attribute]
673pub fn pub_visibility_if_internal(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
674    let mut out = TokenStream::new();
675    let mut item_original: syn::Item = syn::parse(tokens).expect("failed to parse input");
676    let item_modified;
677    let not_internal_feature_gate: Attribute = parse_quote!(#[cfg(not(feature = "internal"))]);
678    let internal_feature_gate: Attribute = parse_quote!(#[cfg(feature = "internal")]);
679    let hide_doc: Attribute = parse_quote!(#[doc(hidden)]);
680    let allow_dead_code: Attribute = parse_quote!(#[allow(dead_code)]);
681    match &mut item_original {
682        Item::Fn(item_fn) => {
683            let mut item_fn_modified = item_fn.clone();
684            item_fn_modified.vis = syn::Visibility::Public(syn::token::Pub(item_fn.span()));
685            item_fn_modified
686                .attrs
687                .splice(0..0, vec![internal_feature_gate, hide_doc, allow_dead_code]);
688            item_modified = Item::Fn(item_fn_modified);
689            item_fn.attrs.splice(0..0, vec![not_internal_feature_gate]);
690        }
691        Item::Struct(item_struct) => {
692            let mut item_struct_modified = item_struct.clone();
693            item_struct_modified
694                .attrs
695                .splice(0..0, vec![internal_feature_gate, hide_doc, allow_dead_code]);
696            item_struct_modified.vis = syn::Visibility::Public(syn::token::Pub(item_struct.span()));
697            item_modified = Item::Struct(item_struct_modified);
698            item_struct
699                .attrs
700                .splice(0..0, vec![not_internal_feature_gate]);
701        }
702        Item::Type(item_type) => {
703            let mut item_type_modified = item_type.clone();
704            item_type_modified
705                .attrs
706                .splice(0..0, vec![internal_feature_gate, hide_doc, allow_dead_code]);
707            item_type_modified.vis = syn::Visibility::Public(syn::token::Pub(item_type.span()));
708            item_modified = Item::Type(item_type_modified);
709            item_type
710                .attrs
711                .splice(0..0, vec![not_internal_feature_gate]);
712        }
713        _ => panic!("pub_visibility_if_internal only works with struct, type and fn"),
714    }
715    let ts: TokenStream = item_original.into_token_stream().into();
716    out.extend(ts);
717    let ts: TokenStream = item_modified.into_token_stream().into();
718    out.extend(ts);
719    out
720}
721
722// applies zenoh_macros::internal if unstable feature is disabled, otherwise applies zenoh_macros::unstable
723#[proc_macro_attribute]
724pub fn internal_or_unstable(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
725    let mut out = TokenStream::new();
726    let mut item_original: syn::Item = syn::parse(tokens).expect("failed to parse input");
727    let item_modified;
728    let non_unstable_feature_gate: Attribute = parse_quote!(#[cfg(not(feature = "unstable"))]);
729    let internal_feature_gate: Attribute = parse_quote!(#[zenoh_macros::internal]);
730    let unstable_feature_gate: Attribute = parse_quote!(#[zenoh_macros::unstable]);
731    match &mut item_original {
732        Item::Fn(item_fn) => {
733            let mut item_fn_modified = item_fn.clone();
734            item_fn_modified.vis = syn::Visibility::Public(syn::token::Pub(item_fn.span()));
735            item_fn_modified
736                .attrs
737                .splice(0..0, vec![non_unstable_feature_gate, internal_feature_gate]);
738            item_modified = Item::Fn(item_fn_modified);
739            item_fn.attrs.splice(0..0, vec![unstable_feature_gate]);
740        }
741        Item::Struct(item_struct) => {
742            let mut item_struct_modified = item_struct.clone();
743            item_struct_modified
744                .attrs
745                .splice(0..0, vec![non_unstable_feature_gate, internal_feature_gate]);
746            item_struct_modified.vis = syn::Visibility::Public(syn::token::Pub(item_struct.span()));
747            item_modified = Item::Struct(item_struct_modified);
748            item_struct.attrs.splice(0..0, vec![unstable_feature_gate]);
749        }
750        Item::Type(item_type) => {
751            let mut item_type_modified = item_type.clone();
752            item_type_modified
753                .attrs
754                .splice(0..0, vec![non_unstable_feature_gate, internal_feature_gate]);
755            item_type_modified.vis = syn::Visibility::Public(syn::token::Pub(item_type.span()));
756            item_modified = Item::Type(item_type_modified);
757            item_type.attrs.splice(0..0, vec![unstable_feature_gate]);
758        }
759        _ => panic!("internal_or_unstable only works with struct, type and fn"),
760    }
761    let ts: TokenStream = item_original.into_token_stream().into();
762    out.extend(ts);
763    let ts: TokenStream = item_modified.into_token_stream().into();
764    out.extend(ts);
765    out
766}