1#![warn(
2 missing_docs,
3 missing_copy_implementations,
4 missing_debug_implementations
5)]
6
7use std::collections::HashSet;
12
13use proc_macro::TokenStream;
14use quote::quote;
15use syn::spanned::Spanned;
16
17#[derive(Default)]
18struct PluginRegister {
19 version: Option<String>,
20 description: Option<String>,
21}
22
23impl syn::parse::Parse for PluginRegister {
24 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
25 const EXPECTED_KEYS: &[&str] = &["version", "description"];
26
27 let mut info = PluginRegister::default();
28 let mut seen_keys = HashSet::new();
29 loop {
30 if input.is_empty() {
31 break;
32 }
33 let key = syn::Ident::parse(input)?.to_string();
34
35 if seen_keys.contains(&key) {
36 panic!("Duplicated key \"{key}\". Keys can only be specified once.");
37 }
38
39 input.parse::<syn::Token![:]>()?;
40
41 match key.as_str() {
42 "version" => {
43 info.version = Some(<syn::LitStr as syn::parse::Parse>::parse(input)?.value())
44 }
45 "description" => {
46 info.description =
47 Some(<syn::LitStr as syn::parse::Parse>::parse(input)?.value())
48 }
49 _ => {
50 return Err(syn::Error::new(
51 key.span(),
52 format!("Unknown key \"{key}\". Valid keys are: {EXPECTED_KEYS:?}."),
53 ))
54 }
55 }
56
57 input.parse::<syn::Token![,]>()?;
58
59 seen_keys.insert(key);
60 }
61 Ok(info)
62 }
63}
64
65#[proc_macro]
84pub fn vlib_plugin_register(ts: TokenStream) -> TokenStream {
85 let PluginRegister {
86 version,
87 description,
88 } = syn::parse_macro_input!(ts as PluginRegister);
89
90 let mut version_elems = version
91 .expect("Missing required attribute \"version\"")
92 .into_bytes();
93 if version_elems.len() > 63 {
94 panic!("Version string exceeds limit of 63 characters");
95 }
96 version_elems.extend(std::iter::repeat_n(0, (version_elems.len()..64).count()));
98 let description = if let Some(description) = description {
99 let description = format!("{}\0", description);
100 quote!(#description.as_ptr() as *const ::std::os::raw::c_char)
101 } else {
102 quote!(std::ptr::null_mut())
103 };
104
105 let output = quote!(
106 #[doc(hidden)]
107 #[link_section = ".vlib_plugin_registration"]
108 #[no_mangle]
109 #[allow(non_upper_case_globals, non_snake_case)]
110 #[used]
111 pub static mut vlib_plugin_registration: ::vpp_plugin::bindings::vlib_plugin_registration_t = ::vpp_plugin::bindings::vlib_plugin_registration_t {
112 cacheline0: ::vpp_plugin::bindings::__IncompleteArrayField::new(),
113 _bitfield_align_1: [0; 0],
114 _bitfield_1: ::vpp_plugin::bindings::__BindgenBitfieldUnit::new([0; 1]),
115 version: [#(#version_elems as ::std::os::raw::c_char),*],
116 version_required: [0; 64],
117 overrides: [0; 256],
118 early_init: ::std::ptr::null_mut(),
119 description: #description,
120 };
121 );
122
123 output.into()
126}
127
128#[proc_macro_attribute]
144pub fn vlib_init_function(_attribute: TokenStream, function: TokenStream) -> TokenStream {
145 let item: syn::Item = syn::parse_macro_input!(function);
146 if let syn::Item::Fn(function) = item {
147 let syn::ItemFn {
148 attrs,
149 block,
150 vis,
151 sig:
152 syn::Signature {
153 ident,
154 unsafety,
155 constness,
156 abi,
157 inputs,
158 output,
159 ..
160 },
161 ..
162 } = function;
163
164 match vis {
166 syn::Visibility::Inherited => {}
167 _ => panic!("#[vlib_init_function] methods must not have visibility modifiers"),
168 }
169
170 let init_fn_ident = syn::parse_str::<syn::Ident>(format!("__{}", ident).as_ref())
171 .expect("Unable to create identifier");
172 let ctor_fn_ident = syn::parse_str::<syn::Ident>(
173 format!("__vlib_add_init_function_init_{}", ident).as_ref(),
174 )
175 .expect("Unable to create identifier");
176 let dtor_fn_ident =
177 syn::parse_str::<syn::Ident>(format!("__vlib_rm_init_function_{}", ident).as_ref())
178 .expect("Unable to create identifier");
179 let init_list_elt_ident =
180 syn::parse_str::<syn::Ident>(format!("_VLIB_INIT_FUNCTION_INIT_{}", ident).as_ref())
181 .expect("Unable to create identifier");
182 let ident_lit = format!("{}\0", ident);
183
184 let output = quote!(
185 #(#attrs)*
186 #vis #unsafety #abi #constness fn #ident(#inputs) #output #block
187
188 unsafe extern "C" fn #init_fn_ident(vm: *mut ::vpp_plugin::bindings::vlib_main_t) -> *mut ::vpp_plugin::bindings::clib_error_t
189 {
190 if let Err(e) = #ident(::vpp_plugin::vlib::BarrierHeldMainRef::from_ptr_mut(vm)) {
191 e.into_raw()
192 } else {
193 std::ptr::null_mut()
194 }
195 }
196
197 static mut #init_list_elt_ident: ::vpp_plugin::bindings::_vlib_init_function_list_elt_t = ::vpp_plugin::bindings::_vlib_init_function_list_elt_t {
198 f: None,
199 name: std::ptr::null_mut(),
200 next_init_function: std::ptr::null_mut(),
201 runs_before: std::ptr::null_mut(),
202 runs_after: std::ptr::null_mut(),
203 init_order: std::ptr::null_mut(),
204 };
205
206 #[::vpp_plugin::macro_support::ctor::ctor(crate_path = ::vpp_plugin::macro_support::ctor)]
207 unsafe fn #ctor_fn_ident () {
208 unsafe {
209 let vgm = ::vpp_plugin::bindings::vlib_helper_get_global_main();
210 #init_list_elt_ident.next_init_function = (*vgm).init_function_registrations;
211 (*vgm).init_function_registrations = std::ptr::addr_of_mut!(#init_list_elt_ident);
212 #init_list_elt_ident.f = Some(#init_fn_ident);
213 #init_list_elt_ident.name = #ident_lit.as_ptr() as *mut ::std::os::raw::c_char;
214 }
215 }
216
217 #[::vpp_plugin::macro_support::ctor::dtor(crate_path = ::vpp_plugin::macro_support::ctor)]
218 unsafe fn #dtor_fn_ident() {
219 let vgm = ::vpp_plugin::bindings::vlib_helper_get_global_main();
220
221 let mut this = (*vgm).init_function_registrations;
222 if this.is_null() {
223 return;
224 }
225 if this == std::ptr::addr_of_mut!(#init_list_elt_ident) {
226 (*vgm).init_function_registrations = (*this).next_init_function;
227 return;
228 }
229
230 let mut prev = this;
231 this = (*this).next_init_function;
232 while !this.is_null() {
233 if this == std::ptr::addr_of_mut!(#init_list_elt_ident) {
234 (*prev).next_init_function = (*this).next_init_function;
235 return;
236 }
237 prev = this;
238 this = (*this).next_init_function;
239 }
240 }
241 );
242
243 output.into()
246 } else {
247 panic!("#[vlib_init_function] items must be functions");
248 }
249}
250
251#[proc_macro_derive(NextNodes, attributes(next_node))]
271pub fn derive_next_nodes(input: TokenStream) -> TokenStream {
272 let input = syn::parse_macro_input!(input as syn::DeriveInput);
273 let syn::DeriveInput { ident, data, .. } = input;
274 if let syn::Data::Enum(data) = data {
275 let mut next_nodes = vec![];
276 for variant in &data.variants {
277 if let Some((_, exp_discriminant)) = &variant.discriminant {
278 return syn::Error::new_spanned(
279 exp_discriminant,
280 "Use of explicit discriminants not allowed",
281 )
282 .into_compile_error()
283 .into();
284 }
285 if !matches!(variant.fields, syn::Fields::Unit) {
286 return syn::Error::new_spanned(&variant.fields, "Only unit variants can be used")
287 .into_compile_error()
288 .into();
289 }
290 let next_node_attr = variant
291 .attrs
292 .iter()
293 .find(|x| x.path().is_ident("next_node"))
294 .expect("Missing attribute \"next_node\"");
295 let syn::Meta::NameValue(next_node_name_value) = &next_node_attr.meta else {
296 return syn::Error::new_spanned(next_node_attr, "Unsupported \"next_node\" attribute syntax. Should be #[next_node = \"<node-name>\".").into_compile_error().into();
297 };
298 let syn::Expr::Lit(next_node) = &next_node_name_value.value else {
299 return syn::Error::new_spanned(next_node_name_value, "Unsupported \"next_node\" attribute syntax. Should be #[next_node = \"<node-name>\".").into_compile_error().into();
300 };
301 let syn::Lit::Str(next_node) = &next_node.lit else {
302 return syn::Error::new_spanned(next_node, "Unsupported \"next_node\" attribute syntax. Should be #[next_node = \"<node-name>\".").into_compile_error().into();
303 };
304 next_nodes.push(format!("{}\0", next_node.value()));
305 }
306 let n_next_nodes = data.variants.len();
307
308 let output = quote!(
309 #[automatically_derived]
310 unsafe impl ::vpp_plugin::vlib::node::NextNodes for #ident {
311 type CNamesArray = [*mut ::std::os::raw::c_char; #n_next_nodes];
312 const C_NAMES: Self::CNamesArray = [#(#next_nodes.as_ptr() as *mut ::std::os::raw::c_char),*];
313
314 fn into_u16(self) -> u16 {
315 self as u16
316 }
317 }
318 );
319
320 output.into()
323 } else {
324 panic!("#[derive(NextNodes)] can only be used on enums");
325 }
326}
327
328struct ErrorCounterAttribute {
329 name: Option<String>,
330 description: String,
331 severity: syn::Ident,
332}
333
334impl syn::parse::Parse for ErrorCounterAttribute {
335 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
336 const VALID_SEVERITIES: &[&str] = &["INFO", "WARNING", "ERROR", "CRITICAL"];
337
338 let mut name_or_description_kw = syn::Ident::parse(input)?;
339
340 let name = if name_or_description_kw == "name" {
341 input.parse::<syn::Token![=]>()?;
342 let name = <syn::LitStr as syn::parse::Parse>::parse(input)?.value();
343 input.parse::<syn::Token![,]>()?;
344 name_or_description_kw = syn::Ident::parse(input)?;
345 Some(name)
346 } else {
347 None
348 };
349
350 if name_or_description_kw != "description" {
351 return Err(syn::Error::new(
352 name_or_description_kw.span(),
353 "Expected: description = \"<...>\", severity = <severity>".to_string(),
354 ));
355 }
356
357 input.parse::<syn::Token![=]>()?;
358 let description = <syn::LitStr as syn::parse::Parse>::parse(input)?.value();
359
360 input.parse::<syn::Token![,]>()?;
361
362 let severity_kw = syn::Ident::parse(input)?;
363
364 if severity_kw != "severity" {
365 return Err(syn::Error::new(
366 severity_kw.span(),
367 "Expected: description = \"<...>\", severity = <severity>".to_string(),
368 ));
369 }
370
371 input.parse::<syn::Token![=]>()?;
372 let severity = syn::Ident::parse(input)?;
373 if !VALID_SEVERITIES.contains(&severity.to_string().as_str()) {
374 return Err(syn::Error::new(
375 severity.span(),
376 format!(
377 "Invalid severity \"{}\". Valid severities are: {:?}.",
378 severity, VALID_SEVERITIES
379 ),
380 ));
381 }
382
383 Ok(Self {
384 name,
385 description,
386 severity,
387 })
388 }
389}
390
391#[proc_macro_derive(ErrorCounters, attributes(error_counter))]
414pub fn derive_error_counters(input: TokenStream) -> TokenStream {
415 let input = syn::parse_macro_input!(input as syn::DeriveInput);
416 let syn::DeriveInput { ident, data, .. } = input;
417 if let syn::Data::Enum(data) = data {
418 let mut error_counter_desc = vec![];
419 for variant in &data.variants {
420 if let Some((_, exp_discriminant)) = &variant.discriminant {
421 return syn::Error::new_spanned(
422 exp_discriminant,
423 "Use of explicit discriminants not allowed",
424 )
425 .into_compile_error()
426 .into();
427 }
428 if !matches!(variant.fields, syn::Fields::Unit) {
429 return syn::Error::new_spanned(&variant.fields, "Only unit variants can be used")
430 .into_compile_error()
431 .into();
432 }
433 let error_counter_attr = variant
434 .attrs
435 .iter()
436 .find(|x| x.path().is_ident("error_counter"))
437 .expect("Missing attribute \"error_counter\"");
438 let syn::Meta::List(error_counter_list) = &error_counter_attr.meta else {
439 return syn::Error::new_spanned(error_counter_attr, "Unsupported \"error_counter\" attribute syntax. Should be #[error_counter(description = \"...\")]").into_compile_error().into();
440 };
441 let ErrorCounterAttribute {
442 name,
443 description,
444 severity,
445 } = match syn::parse2::<ErrorCounterAttribute>(error_counter_list.tokens.clone()) {
446 Ok(v) => v,
447 Err(e) => return proc_macro::TokenStream::from(e.to_compile_error()),
448 };
449 let name = if let Some(name) = name {
450 format!("{}\0", name)
451 } else {
452 format!("{}\0", variant.ident)
453 };
454 let description = format!("{}\0", description);
455 let severity = syn::parse_str::<syn::Ident>(&format!(
456 "vl_counter_severity_e_VL_COUNTER_SEVERITY_{}",
457 severity
458 ))
459 .expect("Unable to create identifier");
460
461 error_counter_desc.push(quote!(
462 ::vpp_plugin::bindings::vlib_error_desc_t {
463 name: #name.as_ptr() as *mut std::ffi::c_char,
464 desc: #description.as_ptr() as *mut std::ffi::c_char,
465 severity: ::vpp_plugin::bindings::#severity,
466 stats_entry_index: 0,
467 },
468 ));
469 }
470 let n_error_counters = data.variants.len();
471
472 let output = quote!(
473 #[automatically_derived]
474 unsafe impl ::vpp_plugin::vlib::node::ErrorCounters for #ident {
475 type CDescriptionsArray = [::vpp_plugin::bindings::vlib_error_desc_t; #n_error_counters];
476 const C_DESCRIPTIONS: Self::CDescriptionsArray = [
477 #(#error_counter_desc)*
478 ];
479
480 fn into_u16(self) -> u16 {
481 self as u16
482 }
483 }
484 );
485
486 output.into()
489 } else {
490 panic!("#[derive(ErrorCounters)] can only be used on enums");
491 }
492}
493
494const CPU_MARCH_TO_CPU_AND_TARGET_FEATURE: &[(&str, Option<&str>, Option<&str>)] = &[
495 ("scalar", None, None),
496 ("hsw", Some("x86_64"), Some("avx2")),
497 ("skx", Some("x86_64"), Some("avx512f")),
498 ("icl", Some("x86_64"), Some("avx512bitalg")),
499];
500
501#[derive(Default)]
502struct Node {
503 name: Option<syn::LitStr>,
504 instance: Option<syn::Ident>,
505 runtime_data_default: Option<syn::Ident>,
506 format_trace: Option<syn::Ident>,
507}
508
509impl Node {
510 fn parse(&mut self, meta: syn::meta::ParseNestedMeta) -> Result<(), syn::Error> {
511 const EXPECTED_KEYS: &[&str] =
512 &["name", "instance", "runtime_data_default", "format_trace"];
513
514 if meta.path.is_ident("name") {
515 self.name = Some(meta.value()?.parse()?);
516 Ok(())
517 } else if meta.path.is_ident("instance") {
518 self.instance = Some(meta.value()?.parse()?);
519 Ok(())
520 } else if meta.path.is_ident("runtime_data_default") {
521 self.runtime_data_default = Some(meta.value()?.parse()?);
522 Ok(())
523 } else if meta.path.is_ident("format_trace") {
524 self.format_trace = Some(meta.value()?.parse()?);
525 Ok(())
526 } else {
527 Err(syn::Error::new(
528 meta.path.span(),
529 format!(
530 "Unknown attribute \"{:?}\". Valid keys are: {EXPECTED_KEYS:?}.",
531 meta.path.get_ident()
532 ),
533 ))
534 }
535 }
536}
537
538#[proc_macro_attribute]
630pub fn vlib_node(attributes: TokenStream, s: TokenStream) -> TokenStream {
631 let mut attrs = Node::default();
632 let node_parser = syn::meta::parser(|meta| attrs.parse(meta));
633 syn::parse_macro_input!(attributes with node_parser);
634 let Node {
635 name,
636 instance,
637 runtime_data_default,
638 format_trace,
639 } = attrs;
640 let item: syn::Item = syn::parse_macro_input!(s);
641 if let syn::Item::Struct(syn::ItemStruct {
642 attrs,
643 vis,
644 struct_token,
645 ident,
646 generics,
647 fields,
648 semi_token,
649 }) = item
650 {
651 let name = name.expect("Missing attribute \"name\". This is required.");
652 let name_lit = format!("{}\0", name.value());
653 let instance = instance.expect("Missing attribute \"instance\". This is required.");
654 let reg_ident =
655 syn::parse_str::<syn::Ident>(format!("{}_NODE_REGISTRATION", ident).as_ref())
656 .expect("Unable to create identifier");
657 let add_node_fn_ident = syn::parse_str::<syn::Ident>(
658 format!("__vlib_add_node_registration_{}", ident).as_ref(),
659 )
660 .expect("Unable to create identifier");
661 let rm_node_fn_ident =
662 syn::parse_str::<syn::Ident>(format!("__vlib_rm_node_registration_{}", ident).as_ref())
663 .expect("Unable to create identifier");
664 let (format_trace_output, format_trace) = match format_trace {
665 Some(format_trace) => {
666 let thunk_format_trace =
667 syn::parse_str::<syn::Ident>(format!("__{}", format_trace).as_ref())
668 .expect("Unable to create identifier");
669 (
670 quote!(
671 unsafe extern "C" fn #thunk_format_trace(s: *mut u8, args: *mut ::vpp_plugin::bindings::va_list) -> *mut u8 {
672 let mut args = std::mem::transmute::<_, ::vpp_plugin::macro_support::va_list::VaList<'_>>(args);
673 let vm = args.get::<*const ::vpp_plugin::bindings::vlib_main_t>().cast_mut();
674 let node = args.get::<*const ::vpp_plugin::bindings::vlib_node_t>().cast_mut();
675 let t = args.get::<*const <#ident as ::vpp_plugin::vlib::node::Node>::TraceData>();
676 let str = #format_trace(&mut ::vpp_plugin::vlib::MainRef::from_ptr_mut(vm), &mut #reg_ident.node_from_ptr(node), &*t);
677 let mut s = ::vpp_plugin::vppinfra::vec::Vec::from_raw(s);
678 s.extend(str.as_bytes());
679 s.into_raw()
680 }
681 ),
682 quote!(Some(#thunk_format_trace)),
683 )
684 }
685 None => (quote!(), quote!(None)),
686 };
687 let runtime_data = match runtime_data_default {
688 Some(runtime_data_default) => {
689 quote!(::std::ptr::addr_of!(#runtime_data_default) as *mut ::std::os::raw::c_void)
691 }
692 None => quote!({
693 ::vpp_plugin::const_assert!(::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::RuntimeData>() == 0);
694 std::ptr::null_mut()
695 }),
696 };
697
698 let mut march_outputs = vec![];
699
700 for (cpu_march, cpu, target_feature) in CPU_MARCH_TO_CPU_AND_TARGET_FEATURE {
701 let raw_node_fn_ident =
702 syn::parse_str::<syn::Ident>(format!("__{}_{}", ident, cpu_march).as_ref())
703 .expect("Unable to create identifier");
704 let registration_ident = syn::parse_str::<syn::Ident>(
705 format!("{}_FN_REGISTRATION_{}", ident, cpu_march).as_ref(),
706 )
707 .expect("Unable to create identifier");
708 let reg_fn_ident = syn::parse_str::<syn::Ident>(
709 format!("{}_multiarch_register_{}", ident, cpu_march).as_ref(),
710 )
711 .expect("Unable to create identifier");
712 let march_variant_ident = syn::parse_str::<syn::Ident>(
713 format!(
714 "clib_march_variant_type_t_CLIB_MARCH_VARIANT_TYPE_{}",
715 cpu_march
716 )
717 .as_ref(),
718 )
719 .expect("Unable to create identifier");
720 let cpu_condition = if let Some(cpu) = cpu {
721 quote!(#[cfg(target_arch = #cpu)])
722 } else {
723 quote!()
724 };
725 let target_feature = if let Some(target_feature) = target_feature {
726 quote!(#[target_feature(enable = #target_feature)])
727 } else {
728 quote!()
729 };
730
731 let output = quote!(
732 #cpu_condition
733 #target_feature
734 #[doc(hidden)]
735 unsafe extern "C" fn #raw_node_fn_ident(
736 vm: *mut ::vpp_plugin::bindings::vlib_main_t,
737 node: *mut ::vpp_plugin::bindings::vlib_node_runtime_t,
738 frame: *mut ::vpp_plugin::bindings::vlib_frame_t,
739 ) -> ::vpp_plugin::bindings::uword {
740 unsafe {
741 <#ident as ::vpp_plugin::vlib::node::Node>::function(
742 &#instance,
743 ::vpp_plugin::vlib::MainRef::from_ptr_mut(vm),
744 #reg_ident.node_runtime_from_ptr(node),
745 #reg_ident.frame_from_ptr(frame),
746 )
747 }.into()
748 }
749
750 #cpu_condition
751 #[doc(hidden)]
752 static mut #registration_ident: ::vpp_plugin::bindings::vlib_node_fn_registration_t = ::vpp_plugin::bindings::vlib_node_fn_registration_t{
753 function: Some(#raw_node_fn_ident),
754 march_variant: ::vpp_plugin::bindings::#march_variant_ident,
755 next_registration: std::ptr::null_mut(),
756 };
757
758 #cpu_condition
759 #[doc(hidden)]
760 #[::vpp_plugin::macro_support::ctor::ctor(crate_path = ::vpp_plugin::macro_support::ctor)]
761 fn #reg_fn_ident() {
762 unsafe {
763 #reg_ident.register_node_fn(std::ptr::addr_of!(#registration_ident).cast_mut());
764 }
765 }
766 );
767 march_outputs.push(output);
768 }
769
770 let output = quote!(
771 #(#attrs)*
772 #vis #struct_token #ident #generics #fields #semi_token
773
774 #[::vpp_plugin::macro_support::ctor::ctor(crate_path = ::vpp_plugin::macro_support::ctor)]
775 fn #add_node_fn_ident() {
776 unsafe {
777 #reg_ident.register();
778 }
779 }
780
781 #[::vpp_plugin::macro_support::ctor::dtor(crate_path = ::vpp_plugin::macro_support::ctor)]
782 fn #rm_node_fn_ident() {
783 unsafe {
784 #reg_ident.unregister();
785 }
786 }
787
788 #format_trace_output
789
790 static #reg_ident: ::vpp_plugin::vlib::node::NodeRegistration<#ident, { <<#ident as ::vpp_plugin::vlib::node::Node>::NextNodes as ::vpp_plugin::vlib::node::NextNodes>::C_NAMES.len() } > = ::vpp_plugin::vlib::node::NodeRegistration::new(
791 ::vpp_plugin::bindings::_vlib_node_registration {
792 function: None,
793 name: #name_lit.as_ptr() as *mut ::std::os::raw::c_char,
794 type_: ::vpp_plugin::bindings::vlib_node_type_t_VLIB_NODE_TYPE_INTERNAL,
795 error_counters: <<#ident as ::vpp_plugin::vlib::node::Node>::Errors as ::vpp_plugin::vlib::node::ErrorCounters>::C_DESCRIPTIONS.as_ptr().cast_mut(),
796 format_trace: #format_trace,
797 runtime_data: #runtime_data,
798 runtime_data_bytes: {
799 ::vpp_plugin::const_assert!(::std::mem::size_of::<<ExampleNode as ::vpp_plugin::vlib::node::Node>::RuntimeData>() <= u8::MAX as usize);
800 ::vpp_plugin::const_assert!(::std::mem::align_of::<<ExampleNode as ::vpp_plugin::vlib::node::Node>::RuntimeData>() <= ::vpp_plugin::vlib::node::RUNTIME_DATA_ALIGN);
801 ::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::RuntimeData>() as u8
802 },
803 vector_size: {
804 ::vpp_plugin::const_assert!(::std::mem::size_of::<<ExampleNode as ::vpp_plugin::vlib::node::Node>::Vector>() <= u8::MAX as usize);
805 ::vpp_plugin::const_assert!(::std::mem::align_of::<<ExampleNode as ::vpp_plugin::vlib::node::Node>::Vector>() <= ::vpp_plugin::vlib::node::FRAME_DATA_ALIGN);
806 ::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Vector>() as u8
807 },
808 aux_size: {
809 ::vpp_plugin::const_assert!(::std::mem::size_of::<<ExampleNode as ::vpp_plugin::vlib::node::Node>::Aux>() <= u8::MAX as usize);
810 ::vpp_plugin::const_assert!(::std::mem::align_of::<<ExampleNode as ::vpp_plugin::vlib::node::Node>::Aux>() <= ::vpp_plugin::vlib::node::FRAME_DATA_ALIGN);
811 ::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Aux>() as u8
812 },
813 scalar_size: {
814 ::vpp_plugin::const_assert!(::std::mem::size_of::<<ExampleNode as ::vpp_plugin::vlib::node::Node>::Scalar>() <= u16::MAX as usize);
815 ::vpp_plugin::const_assert!(::std::mem::align_of::<<ExampleNode as ::vpp_plugin::vlib::node::Node>::Scalar>() <= ::vpp_plugin::vlib::node::FRAME_DATA_ALIGN);
816 ::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Scalar>() as u16
817 },
818 n_errors: <<#ident as ::vpp_plugin::vlib::node::Node>::Errors as ::vpp_plugin::vlib::node::ErrorCounters>::C_DESCRIPTIONS.len()
819 as u16,
820 n_next_nodes: <<#ident as ::vpp_plugin::vlib::node::Node>::NextNodes as ::vpp_plugin::vlib::node::NextNodes>::C_NAMES.len()
821 as u16,
822 next_nodes: <<#ident as ::vpp_plugin::vlib::node::Node>::NextNodes as ::vpp_plugin::vlib::node::NextNodes>::C_NAMES,
823 ..::vpp_plugin::bindings::_vlib_node_registration::new()
824 });
825
826 #(#march_outputs)*
827 );
828
829 output.into()
832 } else {
833 panic!("#[vlib_node] items must be structs");
834 }
835}
836
837struct FeatureInit {
838 identifier: Option<syn::Ident>,
839 arc_name: Option<String>,
840 node: Option<syn::Ident>,
841 runs_before: Vec<String>,
842 runs_after: Vec<String>,
843 feature_data_type: Option<syn::Type>,
844}
845
846impl syn::parse::Parse for FeatureInit {
847 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
848 const EXPECTED_KEYS: &[&str] = &[
849 "identifier",
850 "arc_name",
851 "node",
852 "runs_before",
853 "runs_after",
854 "feature_data_type",
855 ];
856
857 let mut info = FeatureInit {
858 identifier: None,
859 arc_name: None,
860 node: None,
861 runs_before: Vec::new(),
862 runs_after: Vec::new(),
863 feature_data_type: None,
864 };
865 let mut seen_keys = HashSet::new();
866 loop {
867 if input.is_empty() {
868 break;
869 }
870 let key = syn::Ident::parse(input)?.to_string();
871
872 if seen_keys.contains(&key) {
873 panic!("Duplicated key \"{key}\". Keys can only be specified once.");
874 }
875
876 input.parse::<syn::Token![:]>()?;
877
878 match key.as_str() {
879 "identifier" => info.identifier = Some(syn::Ident::parse(input)?),
880 "arc_name" => {
881 info.arc_name = Some(<syn::LitStr as syn::parse::Parse>::parse(input)?.value())
882 }
883 "node" => info.node = Some(syn::Ident::parse(input)?),
884 "runs_before" => {
885 let runs_before_input;
886 syn::bracketed!(runs_before_input in input);
887 let runs_before = syn::punctuated::Punctuated::<syn::LitStr, syn::Token![,]>::parse_terminated(&runs_before_input)?;
888 info.runs_before = runs_before.into_iter().map(|s| s.value()).collect();
889 }
890 "runs_after" => {
891 let runs_after_input;
892 syn::bracketed!(runs_after_input in input);
893 let runs_after = syn::punctuated::Punctuated::<syn::LitStr, syn::Token![,]>::parse_terminated(&runs_after_input)?;
894 info.runs_after = runs_after.into_iter().map(|s| s.value()).collect();
895 }
896 "feature_data_type" => info.feature_data_type = Some(syn::Type::parse(input)?),
897 _ => {
898 return Err(syn::Error::new(
899 key.span(),
900 format!("Unknown key \"{key}\". Valid keys are: {EXPECTED_KEYS:?}."),
901 ))
902 }
903 }
904
905 input.parse::<syn::Token![,]>()?;
906
907 seen_keys.insert(key);
908 }
909 Ok(info)
910 }
911}
912
913#[proc_macro]
990pub fn vnet_feature_init(ts: TokenStream) -> TokenStream {
991 let FeatureInit {
992 identifier,
993 arc_name,
994 node,
995 runs_before,
996 runs_after,
997 feature_data_type,
998 } = syn::parse_macro_input!(ts as FeatureInit);
999
1000 let ident = identifier.expect("Missing key \"identifier\". This is required.");
1001 let ctor_fn_ident =
1002 syn::parse_str::<syn::Ident>(format!("__vnet_add_feature_registration_{}", ident).as_ref())
1003 .expect("Unable to create identifier");
1004 let dtor_fn_ident =
1005 syn::parse_str::<syn::Ident>(format!("__vnet_rm_feature_registration_{}", ident).as_ref())
1006 .expect("Unable to create identifier");
1007 let arc_name = arc_name.expect("Missing key \"arc_name\". This is required.");
1008 let node = node.expect("Missing key \"node\". This is required.");
1009 let reg_ident = syn::parse_str::<syn::Ident>(format!("{}_NODE_REGISTRATION", node).as_ref())
1010 .expect("Unable to create identifier");
1011 let arc_name_lit = format!("{arc_name}\0");
1012 let runs_before_output = if runs_before.is_empty() {
1013 quote!(std::ptr::null_mut())
1014 } else {
1015 let runs_before = runs_before.iter().map(|s| {
1016 let s = format!("{s}\0");
1017 quote!(#s.as_ptr() as *mut ::std::os::raw::c_char)
1018 });
1019 quote!(&[#(#runs_before),*, std::ptr::null_mut()] as *const *mut ::std::os::raw::c_char as *mut *mut ::std::os::raw::c_char)
1020 };
1021 let runs_after_output = if runs_after.is_empty() {
1022 quote!(std::ptr::null_mut())
1023 } else {
1024 let runs_after = runs_after.iter().map(|s| {
1025 let s = format!("{s}\0");
1026 quote!(#s.as_ptr() as *mut ::std::os::raw::c_char)
1027 });
1028 quote!(&[#(#runs_after),*, std::ptr::null_mut()] as *const *mut ::std::os::raw::c_char as *mut *mut ::std::os::raw::c_char)
1029 };
1030 let feature_data_type = feature_data_type
1031 .unwrap_or_else(|| syn::parse_str::<syn::Type>("()").expect("Unable to create identifier"));
1032
1033 let output = quote!(
1034 #[doc(hidden)]
1035 #[::vpp_plugin::macro_support::ctor::ctor(crate_path = ::vpp_plugin::macro_support::ctor)]
1036 fn #ctor_fn_ident() {
1037 unsafe { #ident.register(); }
1038 }
1039
1040 #[doc(hidden)]
1041 #[::vpp_plugin::macro_support::ctor::dtor(crate_path = ::vpp_plugin::macro_support::ctor)]
1042 fn #dtor_fn_ident() {
1043 unsafe { #ident.unregister(); }
1044 }
1045
1046 static #ident: ::vpp_plugin::vnet::feature::FeatureRegistration<#feature_data_type> = unsafe {
1047 ::vpp_plugin::vnet::feature::FeatureRegistration::new(
1048 ::vpp_plugin::bindings::vnet_feature_registration_t {
1049 arc_name: #arc_name_lit.as_ptr() as *mut ::std::os::raw::c_char,
1050 runs_before: #runs_before_output,
1051 runs_after: #runs_after_output,
1052 enable_disable_cb: None,
1053 ..::vpp_plugin::bindings::vnet_feature_registration_t::new()
1054 },
1055 &#reg_ident,
1056 )
1057 };
1058 );
1059
1060 output.into()
1063}
1064
1065#[proc_macro_attribute]
1085pub fn vlib_cli_command(attributes: TokenStream, function: TokenStream) -> TokenStream {
1086 let mut path: Option<syn::LitStr> = None;
1087 let mut short_help: Option<syn::LitStr> = None;
1088 let attr_parser = syn::meta::parser(|meta| {
1089 if meta.path.is_ident("path") {
1090 path = Some(meta.value()?.parse()?);
1091 Ok(())
1092 } else if meta.path.is_ident("short_help") {
1093 short_help = Some(meta.value()?.parse()?);
1094 Ok(())
1095 } else {
1096 Err(meta.error("unsupported vlib_cli_command property"))
1097 }
1098 });
1099
1100 syn::parse_macro_input!(attributes with attr_parser);
1101
1102 let item: syn::Item = syn::parse_macro_input!(function);
1103 if let syn::Item::Fn(function) = item {
1104 let syn::ItemFn {
1105 attrs,
1106 block,
1107 vis,
1108 sig:
1109 syn::Signature {
1110 ident,
1111 unsafety,
1112 constness,
1113 abi,
1114 inputs,
1115 output,
1116 ..
1117 },
1118 ..
1119 } = function;
1120
1121 let path = path.expect(
1122 "Missing attribute 'path'. Example: #[vlib_cli_command(path = \"rust-example\")]",
1123 );
1124 let path = format!("{}\0", path.value());
1125 let short_help = if let Some(short_help) = short_help {
1126 let short_help = format!("{}\0", short_help.value());
1127 quote!(#short_help.as_ptr() as *mut ::std::os::raw::c_char)
1128 } else {
1129 quote!(std::ptr::null_mut())
1130 };
1131 let raw_fn_ident = syn::parse_str::<syn::Ident>(format!("__{}", ident).as_ref())
1132 .expect("Unable to create identifier");
1133 let ctor_fn_ident = syn::parse_str::<syn::Ident>(
1134 format!("__vlib_cli_command_registration_{}", ident).as_ref(),
1135 )
1136 .expect("Unable to create identifier");
1137 let dtor_fn_ident = syn::parse_str::<syn::Ident>(
1138 format!("__vlib_cli_command_unregistration_{}", ident).as_ref(),
1139 )
1140 .expect("Unable to create identifier");
1141 let cli_command_ident =
1142 syn::parse_str::<syn::Ident>(format!("_VLIB_CLI_COMMAND_{}", ident).as_ref())
1143 .expect("Unable to create identifier");
1144
1145 let output = quote!(
1146 #(#attrs)*
1147 #vis #unsafety #abi #constness fn #ident(#inputs) #output #block
1148
1149 #[doc(hidden)]
1150 unsafe extern "C" fn #raw_fn_ident(
1151 vm: *mut ::vpp_plugin::bindings::vlib_main_t,
1152 input: *mut ::vpp_plugin::bindings::unformat_input_t,
1153 _cmd: *mut ::vpp_plugin::bindings::vlib_cli_command_t,
1154 ) -> *mut ::vpp_plugin::bindings::clib_error_t
1155 {
1156 let s = ::vpp_plugin::vppinfra::unformat::raw_unformat_line_input_to_string(input);
1157 if let Err(e) = #ident(::vpp_plugin::vlib::BarrierHeldMainRef::from_ptr_mut(vm), s.as_str()) {
1158 e.into_raw()
1159 } else {
1160 std::ptr::null_mut()
1161 }
1162 }
1163
1164 static #cli_command_ident: ::vpp_plugin::vlib::cli::CommandRegistration = ::vpp_plugin::vlib::cli::CommandRegistration::new(::vpp_plugin::bindings::vlib_cli_command_t {
1165 path: #path.as_ptr() as *mut ::std::os::raw::c_char,
1166 short_help: #short_help,
1167 long_help: std::ptr::null_mut(), function: Some(#raw_fn_ident),
1169 is_mp_safe: 0,
1170 ..::vpp_plugin::bindings::vlib_cli_command_t::new()
1171 });
1172
1173 #[::vpp_plugin::macro_support::ctor::ctor(crate_path = ::vpp_plugin::macro_support::ctor)]
1174 unsafe fn #ctor_fn_ident () {
1175 unsafe {
1176 #cli_command_ident.register();
1177 }
1178 }
1179
1180 #[::vpp_plugin::macro_support::ctor::dtor(crate_path = ::vpp_plugin::macro_support::ctor)]
1181 unsafe fn #dtor_fn_ident() {
1182 unsafe {
1183 #cli_command_ident.unregister();
1184 }
1185 }
1186 );
1187
1188 output.into()
1191 } else {
1192 panic!("#[vlib_cli_command] items must be functions");
1193 }
1194}