1use 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
62enum AnnotableItem {
64 Item(Item),
66 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 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 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]
140pub 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 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]
175pub 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#[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]
217pub 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
237fn 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 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#[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#[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#[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#[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#[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#[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#[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#[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 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 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}