1use 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#[allow(clippy::large_enum_variant)]
67enum AnnotableItem {
68 Item(Item),
70 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 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 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
143fn 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 for attr in old_attrs.by_ref() {
151 attrs.push(attr);
152 if get_doc_str(attrs.last().unwrap()).is_some() {
153 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 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 attrs.extend(old_attrs);
181
182 Ok(())
183}
184
185#[proc_macro_attribute]
186pub 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]
203pub 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]
226pub 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
246fn 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 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#[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#[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#[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#[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#[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#[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#[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#[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 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 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#[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}