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 #[unsafe(link_section = ".vlib_plugin_registration")]
108 #[unsafe(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 version: [#(#version_elems as ::std::os::raw::c_char),*],
113 description: #description,
114 ..::vpp_plugin::bindings::vlib_plugin_registration_t::new()
115 };
116 );
117
118 output.into()
121}
122
123#[proc_macro_attribute]
139pub fn vlib_init_function(_attribute: TokenStream, function: TokenStream) -> TokenStream {
140 let item: syn::Item = syn::parse_macro_input!(function);
141 if let syn::Item::Fn(function) = item {
142 let syn::ItemFn {
143 attrs,
144 block,
145 vis,
146 sig:
147 syn::Signature {
148 ident,
149 unsafety,
150 constness,
151 abi,
152 inputs,
153 output,
154 ..
155 },
156 ..
157 } = function;
158
159 match vis {
161 syn::Visibility::Inherited => {}
162 _ => panic!("#[vlib_init_function] methods must not have visibility modifiers"),
163 }
164
165 let init_fn_ident = syn::parse_str::<syn::Ident>(format!("__{}", ident).as_ref())
166 .expect("Unable to create identifier");
167 let ctor_fn_ident = syn::parse_str::<syn::Ident>(
168 format!("__vlib_add_init_function_init_{}", ident).as_ref(),
169 )
170 .expect("Unable to create identifier");
171 let dtor_fn_ident =
172 syn::parse_str::<syn::Ident>(format!("__vlib_rm_init_function_{}", ident).as_ref())
173 .expect("Unable to create identifier");
174 let init_list_elt_ident =
175 syn::parse_str::<syn::Ident>(format!("_VLIB_INIT_FUNCTION_INIT_{}", ident).as_ref())
176 .expect("Unable to create identifier");
177 let ident_lit = format!("{}\0", ident);
178
179 let output = quote!(
180 #(#attrs)*
181 #vis #unsafety #abi #constness fn #ident(#inputs) #output #block
182
183 unsafe extern "C" fn #init_fn_ident(vm: *mut ::vpp_plugin::bindings::vlib_main_t) -> *mut ::vpp_plugin::bindings::clib_error_t
184 {
185 if let Err(e) = #ident(::vpp_plugin::vlib::BarrierHeldMainRef::from_ptr_mut(vm)) {
186 e.into_raw()
187 } else {
188 std::ptr::null_mut()
189 }
190 }
191
192 static mut #init_list_elt_ident: ::vpp_plugin::bindings::_vlib_init_function_list_elt_t = ::vpp_plugin::bindings::_vlib_init_function_list_elt_t {
193 f: None,
194 name: std::ptr::null_mut(),
195 next_init_function: std::ptr::null_mut(),
196 runs_before: std::ptr::null_mut(),
197 runs_after: std::ptr::null_mut(),
198 init_order: std::ptr::null_mut(),
199 };
200
201 #[::vpp_plugin::macro_support::ctor::ctor(crate_path = ::vpp_plugin::macro_support::ctor)]
202 unsafe fn #ctor_fn_ident () {
203 unsafe {
204 let vgm = ::vpp_plugin::bindings::vlib_helper_get_global_main();
205 #init_list_elt_ident.next_init_function = (*vgm).init_function_registrations;
206 (*vgm).init_function_registrations = std::ptr::addr_of_mut!(#init_list_elt_ident);
207 #init_list_elt_ident.f = Some(#init_fn_ident);
208 #init_list_elt_ident.name = #ident_lit.as_ptr() as *mut ::std::os::raw::c_char;
209 }
210 }
211
212 #[::vpp_plugin::macro_support::ctor::dtor(crate_path = ::vpp_plugin::macro_support::ctor)]
213 unsafe fn #dtor_fn_ident() {
214 let vgm = ::vpp_plugin::bindings::vlib_helper_get_global_main();
215
216 let mut this = (*vgm).init_function_registrations;
217 if this.is_null() {
218 return;
219 }
220 if this == std::ptr::addr_of_mut!(#init_list_elt_ident) {
221 (*vgm).init_function_registrations = (*this).next_init_function;
222 return;
223 }
224
225 let mut prev = this;
226 this = (*this).next_init_function;
227 while !this.is_null() {
228 if this == std::ptr::addr_of_mut!(#init_list_elt_ident) {
229 (*prev).next_init_function = (*this).next_init_function;
230 return;
231 }
232 prev = this;
233 this = (*this).next_init_function;
234 }
235 }
236 );
237
238 output.into()
241 } else {
242 panic!("#[vlib_init_function] items must be functions");
243 }
244}
245
246#[proc_macro_derive(NextNodes, attributes(next_node))]
266pub fn derive_next_nodes(input: TokenStream) -> TokenStream {
267 let input = syn::parse_macro_input!(input as syn::DeriveInput);
268 let syn::DeriveInput { ident, data, .. } = input;
269 if let syn::Data::Enum(data) = data {
270 let mut next_nodes = vec![];
271 for variant in &data.variants {
272 if let Some((_, exp_discriminant)) = &variant.discriminant {
273 return syn::Error::new_spanned(
274 exp_discriminant,
275 "Use of explicit discriminants not allowed",
276 )
277 .into_compile_error()
278 .into();
279 }
280 if !matches!(variant.fields, syn::Fields::Unit) {
281 return syn::Error::new_spanned(&variant.fields, "Only unit variants can be used")
282 .into_compile_error()
283 .into();
284 }
285 let next_node_attr = variant
286 .attrs
287 .iter()
288 .find(|x| x.path().is_ident("next_node"))
289 .expect("Missing attribute \"next_node\"");
290 let syn::Meta::NameValue(next_node_name_value) = &next_node_attr.meta else {
291 return syn::Error::new_spanned(next_node_attr, "Unsupported \"next_node\" attribute syntax. Should be #[next_node = \"<node-name>\".").into_compile_error().into();
292 };
293 let syn::Expr::Lit(next_node) = &next_node_name_value.value else {
294 return syn::Error::new_spanned(next_node_name_value, "Unsupported \"next_node\" attribute syntax. Should be #[next_node = \"<node-name>\".").into_compile_error().into();
295 };
296 let syn::Lit::Str(next_node) = &next_node.lit else {
297 return syn::Error::new_spanned(next_node, "Unsupported \"next_node\" attribute syntax. Should be #[next_node = \"<node-name>\".").into_compile_error().into();
298 };
299 next_nodes.push(format!("{}\0", next_node.value()));
300 }
301 let n_next_nodes = data.variants.len();
302
303 let output = quote!(
304 #[automatically_derived]
305 unsafe impl ::vpp_plugin::vlib::node::NextNodes for #ident {
306 type CNamesArray = [*mut ::std::os::raw::c_char; #n_next_nodes];
307 const C_NAMES: Self::CNamesArray = [#(#next_nodes.as_ptr() as *mut ::std::os::raw::c_char),*];
308
309 fn into_u16(self) -> u16 {
310 self as u16
311 }
312 }
313 );
314
315 output.into()
318 } else {
319 panic!("#[derive(NextNodes)] can only be used on enums");
320 }
321}
322
323struct ErrorCounterAttribute {
324 name: Option<String>,
325 description: String,
326 severity: syn::Ident,
327}
328
329impl syn::parse::Parse for ErrorCounterAttribute {
330 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
331 const VALID_SEVERITIES: &[&str] = &["INFO", "WARNING", "ERROR", "CRITICAL"];
332
333 let mut name_or_description_kw = syn::Ident::parse(input)?;
334
335 let name = if name_or_description_kw == "name" {
336 input.parse::<syn::Token![=]>()?;
337 let name = <syn::LitStr as syn::parse::Parse>::parse(input)?.value();
338 input.parse::<syn::Token![,]>()?;
339 name_or_description_kw = syn::Ident::parse(input)?;
340 Some(name)
341 } else {
342 None
343 };
344
345 if name_or_description_kw != "description" {
346 return Err(syn::Error::new(
347 name_or_description_kw.span(),
348 "Expected: description = \"<...>\", severity = <severity>".to_string(),
349 ));
350 }
351
352 input.parse::<syn::Token![=]>()?;
353 let description = <syn::LitStr as syn::parse::Parse>::parse(input)?.value();
354
355 input.parse::<syn::Token![,]>()?;
356
357 let severity_kw = syn::Ident::parse(input)?;
358
359 if severity_kw != "severity" {
360 return Err(syn::Error::new(
361 severity_kw.span(),
362 "Expected: description = \"<...>\", severity = <severity>".to_string(),
363 ));
364 }
365
366 input.parse::<syn::Token![=]>()?;
367 let severity = syn::Ident::parse(input)?;
368 if !VALID_SEVERITIES.contains(&severity.to_string().as_str()) {
369 return Err(syn::Error::new(
370 severity.span(),
371 format!(
372 "Invalid severity \"{}\". Valid severities are: {:?}.",
373 severity, VALID_SEVERITIES
374 ),
375 ));
376 }
377
378 Ok(Self {
379 name,
380 description,
381 severity,
382 })
383 }
384}
385
386#[proc_macro_derive(ErrorCounters, attributes(error_counter))]
409pub fn derive_error_counters(input: TokenStream) -> TokenStream {
410 let input = syn::parse_macro_input!(input as syn::DeriveInput);
411 let syn::DeriveInput { ident, data, .. } = input;
412 if let syn::Data::Enum(data) = data {
413 let mut error_counter_desc = vec![];
414 for variant in &data.variants {
415 if let Some((_, exp_discriminant)) = &variant.discriminant {
416 return syn::Error::new_spanned(
417 exp_discriminant,
418 "Use of explicit discriminants not allowed",
419 )
420 .into_compile_error()
421 .into();
422 }
423 if !matches!(variant.fields, syn::Fields::Unit) {
424 return syn::Error::new_spanned(&variant.fields, "Only unit variants can be used")
425 .into_compile_error()
426 .into();
427 }
428 let error_counter_attr = variant
429 .attrs
430 .iter()
431 .find(|x| x.path().is_ident("error_counter"))
432 .expect("Missing attribute \"error_counter\"");
433 let syn::Meta::List(error_counter_list) = &error_counter_attr.meta else {
434 return syn::Error::new_spanned(error_counter_attr, "Unsupported \"error_counter\" attribute syntax. Should be #[error_counter(description = \"...\")]").into_compile_error().into();
435 };
436 let ErrorCounterAttribute {
437 name,
438 description,
439 severity,
440 } = match syn::parse2::<ErrorCounterAttribute>(error_counter_list.tokens.clone()) {
441 Ok(v) => v,
442 Err(e) => return proc_macro::TokenStream::from(e.to_compile_error()),
443 };
444 let name = if let Some(name) = name {
445 format!("{}\0", name)
446 } else {
447 format!("{}\0", variant.ident)
448 };
449 let description = format!("{}\0", description);
450 let severity = syn::parse_str::<syn::Ident>(&format!(
451 "vl_counter_severity_e_VL_COUNTER_SEVERITY_{}",
452 severity
453 ))
454 .expect("Unable to create identifier");
455
456 error_counter_desc.push(quote!(
457 ::vpp_plugin::bindings::vlib_error_desc_t {
458 name: #name.as_ptr() as *mut std::ffi::c_char,
459 desc: #description.as_ptr() as *mut std::ffi::c_char,
460 severity: ::vpp_plugin::bindings::#severity,
461 stats_entry_index: 0,
462 },
463 ));
464 }
465 let n_error_counters = data.variants.len();
466
467 let output = quote!(
468 #[automatically_derived]
469 unsafe impl ::vpp_plugin::vlib::node::ErrorCounters for #ident {
470 type CDescriptionsArray = [::vpp_plugin::bindings::vlib_error_desc_t; #n_error_counters];
471 const C_DESCRIPTIONS: Self::CDescriptionsArray = [
472 #(#error_counter_desc)*
473 ];
474
475 fn into_u16(self) -> u16 {
476 self as u16
477 }
478 }
479 );
480
481 output.into()
484 } else {
485 panic!("#[derive(ErrorCounters)] can only be used on enums");
486 }
487}
488
489const CPU_MARCH_TO_CPU_AND_TARGET_FEATURE: &[(&str, Option<&str>, Option<&str>)] = &[
490 ("scalar", Some("x86_64"), None),
491 ("hsw", Some("x86_64"), Some("avx2")),
492 ("skx", Some("x86_64"), Some("avx512f")),
493 ("icl", Some("x86_64"), Some("avx512bitalg")),
494 (
496 "octeontx2",
497 Some("aarch64"),
498 Some("crc,lse,rdm,pan,lor,vh,ras,dpb,sha2,aes"),
499 ),
500 (
502 "thunderx2t99",
503 Some("aarch64"),
504 Some("crc,lse,rdm,pan,lor,vh,sha2,aes"),
505 ),
506 ("cortexa72", Some("aarch64"), Some("crc,sha2,aes")),
507 (
509 "neoversen1",
510 Some("aarch64"),
511 Some("crc,lse,rdm,pan,lor,vh,ras,dpb,sha2,aes"),
512 ),
513 (
515 "neoversen2",
516 Some("aarch64"),
517 Some(
518 "crc,lse,rdm,pan,lor,vh,ras,dpb,rcpc,paca,pacg,jsconv,dotprod,dit,flagm,ssbs,sb,dpb2,bti,sve2",
519 ),
520 ),
521];
522
523#[derive(Default)]
524struct Node {
525 name: Option<syn::LitStr>,
526 instance: Option<syn::Ident>,
527 runtime_data_default: Option<syn::Ident>,
528 format_trace: Option<syn::Ident>,
529}
530
531impl Node {
532 fn parse(&mut self, meta: syn::meta::ParseNestedMeta) -> Result<(), syn::Error> {
533 const EXPECTED_KEYS: &[&str] =
534 &["name", "instance", "runtime_data_default", "format_trace"];
535
536 if meta.path.is_ident("name") {
537 self.name = Some(meta.value()?.parse()?);
538 Ok(())
539 } else if meta.path.is_ident("instance") {
540 self.instance = Some(meta.value()?.parse()?);
541 Ok(())
542 } else if meta.path.is_ident("runtime_data_default") {
543 self.runtime_data_default = Some(meta.value()?.parse()?);
544 Ok(())
545 } else if meta.path.is_ident("format_trace") {
546 self.format_trace = Some(meta.value()?.parse()?);
547 Ok(())
548 } else {
549 Err(syn::Error::new(
550 meta.path.span(),
551 format!(
552 "Unknown attribute \"{:?}\". Valid keys are: {EXPECTED_KEYS:?}.",
553 meta.path.get_ident()
554 ),
555 ))
556 }
557 }
558}
559
560#[proc_macro_attribute]
652pub fn vlib_node(attributes: TokenStream, s: TokenStream) -> TokenStream {
653 let mut attrs = Node::default();
654 let node_parser = syn::meta::parser(|meta| attrs.parse(meta));
655 syn::parse_macro_input!(attributes with node_parser);
656 let Node {
657 name,
658 instance,
659 runtime_data_default,
660 format_trace,
661 } = attrs;
662 let item: syn::Item = syn::parse_macro_input!(s);
663 if let syn::Item::Struct(syn::ItemStruct {
664 attrs,
665 vis,
666 struct_token,
667 ident,
668 generics,
669 fields,
670 semi_token,
671 }) = item
672 {
673 let name = name.expect("Missing attribute \"name\". This is required.");
674 let name_lit = format!("{}\0", name.value());
675 let instance = instance.expect("Missing attribute \"instance\". This is required.");
676 let reg_ident =
677 syn::parse_str::<syn::Ident>(format!("{}_NODE_REGISTRATION", ident).as_ref())
678 .expect("Unable to create identifier");
679 let add_node_fn_ident = syn::parse_str::<syn::Ident>(
680 format!("__vlib_add_node_registration_{}", ident).as_ref(),
681 )
682 .expect("Unable to create identifier");
683 let rm_node_fn_ident =
684 syn::parse_str::<syn::Ident>(format!("__vlib_rm_node_registration_{}", ident).as_ref())
685 .expect("Unable to create identifier");
686 let (format_trace_output, format_trace) = match format_trace {
687 Some(format_trace) => {
688 let thunk_format_trace =
689 syn::parse_str::<syn::Ident>(format!("__{}", format_trace).as_ref())
690 .expect("Unable to create identifier");
691 (
692 quote!(
693 unsafe extern "C" fn #thunk_format_trace(s: *mut u8, args: *mut ::vpp_plugin::bindings::va_list) -> *mut u8 {
694 unsafe {
695 let mut args = std::mem::transmute::<_, ::vpp_plugin::macro_support::va_list::VaList<'_>>(args);
696 let vm = args.get::<*const ::vpp_plugin::bindings::vlib_main_t>().cast_mut();
697 let node = args.get::<*const ::vpp_plugin::bindings::vlib_node_t>().cast_mut();
698 let t = args.get::<*const <#ident as ::vpp_plugin::vlib::node::Node>::TraceData>();
699 let str = #format_trace(&mut ::vpp_plugin::vlib::MainRef::from_ptr_mut(vm), &mut #reg_ident.node_from_ptr(node), &*t);
700 let mut s = ::vpp_plugin::vppinfra::vec::Vec::from_raw(s);
701 s.extend(str.as_bytes());
702 s.into_raw()
703 }
704 }
705 ),
706 quote!(Some(#thunk_format_trace)),
707 )
708 }
709 None => (quote!(), quote!(None)),
710 };
711 let runtime_data = match runtime_data_default {
712 Some(runtime_data_default) => {
713 quote!(::std::ptr::addr_of!(#runtime_data_default) as *mut ::std::os::raw::c_void)
715 }
716 None => quote!({
717 ::vpp_plugin::const_assert!(::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::RuntimeData>() == 0);
718 std::ptr::null_mut()
719 }),
720 };
721
722 let mut march_outputs = vec![];
723
724 for (cpu_march, cpu, target_feature) in CPU_MARCH_TO_CPU_AND_TARGET_FEATURE {
725 let raw_node_fn_ident =
726 syn::parse_str::<syn::Ident>(format!("__{}_{}", ident, cpu_march).as_ref())
727 .expect("Unable to create identifier");
728 let registration_ident = syn::parse_str::<syn::Ident>(
729 format!("{}_FN_REGISTRATION_{}", ident, cpu_march).as_ref(),
730 )
731 .expect("Unable to create identifier");
732 let reg_fn_ident = syn::parse_str::<syn::Ident>(
733 format!("{}_multiarch_register_{}", ident, cpu_march).as_ref(),
734 )
735 .expect("Unable to create identifier");
736 let march_variant_ident = syn::parse_str::<syn::Ident>(
737 format!(
738 "clib_march_variant_type_t_CLIB_MARCH_VARIANT_TYPE_{}",
739 cpu_march
740 )
741 .as_ref(),
742 )
743 .expect("Unable to create identifier");
744 let cpu_condition = if let Some(cpu) = cpu {
745 quote!(#[cfg(target_arch = #cpu)])
746 } else {
747 quote!()
748 };
749 let target_feature = if let Some(target_feature) = target_feature {
750 quote!(#[target_feature(enable = #target_feature)])
751 } else {
752 quote!()
753 };
754
755 let output = quote!(
756 #cpu_condition
757 #target_feature
758 #[doc(hidden)]
759 unsafe extern "C" fn #raw_node_fn_ident(
760 vm: *mut ::vpp_plugin::bindings::vlib_main_t,
761 node: *mut ::vpp_plugin::bindings::vlib_node_runtime_t,
762 frame: *mut ::vpp_plugin::bindings::vlib_frame_t,
763 ) -> ::vpp_plugin::bindings::uword {
764 unsafe {
765 <#ident as ::vpp_plugin::vlib::node::Node>::function(
766 &#instance,
767 ::vpp_plugin::vlib::MainRef::from_ptr_mut(vm),
768 #reg_ident.node_runtime_from_ptr(node),
769 #reg_ident.frame_from_ptr(frame),
770 )
771 }.into()
772 }
773
774 #cpu_condition
775 #[doc(hidden)]
776 static mut #registration_ident: ::vpp_plugin::bindings::vlib_node_fn_registration_t = ::vpp_plugin::bindings::vlib_node_fn_registration_t{
777 function: Some(#raw_node_fn_ident),
778 march_variant: ::vpp_plugin::bindings::#march_variant_ident,
779 next_registration: std::ptr::null_mut(),
780 };
781
782 #cpu_condition
783 #[doc(hidden)]
784 #[::vpp_plugin::macro_support::ctor::ctor(crate_path = ::vpp_plugin::macro_support::ctor)]
785 fn #reg_fn_ident() {
786 unsafe {
787 #reg_ident.register_node_fn(std::ptr::addr_of!(#registration_ident).cast_mut());
788 }
789 }
790 );
791 march_outputs.push(output);
792 }
793
794 let output = quote!(
795 #(#attrs)*
796 #vis #struct_token #ident #generics #fields #semi_token
797
798 #[::vpp_plugin::macro_support::ctor::ctor(crate_path = ::vpp_plugin::macro_support::ctor)]
799 fn #add_node_fn_ident() {
800 unsafe {
801 #reg_ident.register();
802 }
803 }
804
805 #[::vpp_plugin::macro_support::ctor::dtor(crate_path = ::vpp_plugin::macro_support::ctor)]
806 fn #rm_node_fn_ident() {
807 unsafe {
808 #reg_ident.unregister();
809 }
810 }
811
812 #format_trace_output
813
814 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(
815 ::vpp_plugin::bindings::_vlib_node_registration {
816 function: None,
817 name: #name_lit.as_ptr() as *mut ::std::os::raw::c_char,
818 type_: ::vpp_plugin::bindings::vlib_node_type_t_VLIB_NODE_TYPE_INTERNAL,
819 error_counters: <<#ident as ::vpp_plugin::vlib::node::Node>::Errors as ::vpp_plugin::vlib::node::ErrorCounters>::C_DESCRIPTIONS.as_ptr().cast_mut(),
820 format_trace: #format_trace,
821 runtime_data: #runtime_data,
822 runtime_data_bytes: {
823 ::vpp_plugin::const_assert!(::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::RuntimeData>() <= u8::MAX as usize);
824 ::vpp_plugin::const_assert!(::std::mem::align_of::<<#ident as ::vpp_plugin::vlib::node::Node>::RuntimeData>() <= ::vpp_plugin::vlib::node::RUNTIME_DATA_ALIGN);
825 ::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::RuntimeData>() as u8
826 },
827 vector_size: {
828 ::vpp_plugin::const_assert!(::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Vector>() <= u8::MAX as usize);
829 ::vpp_plugin::const_assert!(::std::mem::align_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Vector>() <= ::vpp_plugin::vlib::node::FRAME_DATA_ALIGN);
830 ::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Vector>() as u8
831 },
832 aux_size: {
833 ::vpp_plugin::const_assert!(::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Aux>() <= u8::MAX as usize);
834 ::vpp_plugin::const_assert!(::std::mem::align_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Aux>() <= ::vpp_plugin::vlib::node::FRAME_DATA_ALIGN);
835 ::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Aux>() as u8
836 },
837 scalar_size: {
838 ::vpp_plugin::const_assert!(::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Scalar>() <= u16::MAX as usize);
839 ::vpp_plugin::const_assert!(::std::mem::align_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Scalar>() <= ::vpp_plugin::vlib::node::FRAME_DATA_ALIGN);
840 ::std::mem::size_of::<<#ident as ::vpp_plugin::vlib::node::Node>::Scalar>() as u16
841 },
842 n_errors: <<#ident as ::vpp_plugin::vlib::node::Node>::Errors as ::vpp_plugin::vlib::node::ErrorCounters>::C_DESCRIPTIONS.len()
843 as u16,
844 n_next_nodes: <<#ident as ::vpp_plugin::vlib::node::Node>::NextNodes as ::vpp_plugin::vlib::node::NextNodes>::C_NAMES.len()
845 as u16,
846 next_nodes: <<#ident as ::vpp_plugin::vlib::node::Node>::NextNodes as ::vpp_plugin::vlib::node::NextNodes>::C_NAMES,
847 ..::vpp_plugin::bindings::_vlib_node_registration::new()
848 });
849
850 #(#march_outputs)*
851 );
852
853 output.into()
856 } else {
857 panic!("#[vlib_node] items must be structs");
858 }
859}
860
861struct FeatureInit {
862 identifier: Option<syn::Ident>,
863 arc_name: Option<String>,
864 node: Option<syn::Ident>,
865 runs_before: Vec<String>,
866 runs_after: Vec<String>,
867 feature_data_type: Option<syn::Type>,
868}
869
870impl syn::parse::Parse for FeatureInit {
871 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
872 const EXPECTED_KEYS: &[&str] = &[
873 "identifier",
874 "arc_name",
875 "node",
876 "runs_before",
877 "runs_after",
878 "feature_data_type",
879 ];
880
881 let mut info = FeatureInit {
882 identifier: None,
883 arc_name: None,
884 node: None,
885 runs_before: Vec::new(),
886 runs_after: Vec::new(),
887 feature_data_type: None,
888 };
889 let mut seen_keys = HashSet::new();
890 loop {
891 if input.is_empty() {
892 break;
893 }
894 let key = syn::Ident::parse(input)?.to_string();
895
896 if seen_keys.contains(&key) {
897 panic!("Duplicated key \"{key}\". Keys can only be specified once.");
898 }
899
900 input.parse::<syn::Token![:]>()?;
901
902 match key.as_str() {
903 "identifier" => info.identifier = Some(syn::Ident::parse(input)?),
904 "arc_name" => {
905 info.arc_name = Some(<syn::LitStr as syn::parse::Parse>::parse(input)?.value())
906 }
907 "node" => info.node = Some(syn::Ident::parse(input)?),
908 "runs_before" => {
909 let runs_before_input;
910 syn::bracketed!(runs_before_input in input);
911 let runs_before = syn::punctuated::Punctuated::<syn::LitStr, syn::Token![,]>::parse_terminated(&runs_before_input)?;
912 info.runs_before = runs_before.into_iter().map(|s| s.value()).collect();
913 }
914 "runs_after" => {
915 let runs_after_input;
916 syn::bracketed!(runs_after_input in input);
917 let runs_after = syn::punctuated::Punctuated::<syn::LitStr, syn::Token![,]>::parse_terminated(&runs_after_input)?;
918 info.runs_after = runs_after.into_iter().map(|s| s.value()).collect();
919 }
920 "feature_data_type" => info.feature_data_type = Some(syn::Type::parse(input)?),
921 _ => {
922 return Err(syn::Error::new(
923 key.span(),
924 format!("Unknown key \"{key}\". Valid keys are: {EXPECTED_KEYS:?}."),
925 ));
926 }
927 }
928
929 input.parse::<syn::Token![,]>()?;
930
931 seen_keys.insert(key);
932 }
933 Ok(info)
934 }
935}
936
937#[proc_macro]
1014pub fn vnet_feature_init(ts: TokenStream) -> TokenStream {
1015 let FeatureInit {
1016 identifier,
1017 arc_name,
1018 node,
1019 runs_before,
1020 runs_after,
1021 feature_data_type,
1022 } = syn::parse_macro_input!(ts as FeatureInit);
1023
1024 let ident = identifier.expect("Missing key \"identifier\". This is required.");
1025 let ctor_fn_ident =
1026 syn::parse_str::<syn::Ident>(format!("__vnet_add_feature_registration_{}", ident).as_ref())
1027 .expect("Unable to create identifier");
1028 let dtor_fn_ident =
1029 syn::parse_str::<syn::Ident>(format!("__vnet_rm_feature_registration_{}", ident).as_ref())
1030 .expect("Unable to create identifier");
1031 let arc_name = arc_name.expect("Missing key \"arc_name\". This is required.");
1032 let node = node.expect("Missing key \"node\". This is required.");
1033 let reg_ident = syn::parse_str::<syn::Ident>(format!("{}_NODE_REGISTRATION", node).as_ref())
1034 .expect("Unable to create identifier");
1035 let arc_name_lit = format!("{arc_name}\0");
1036 let runs_before_output = if runs_before.is_empty() {
1037 quote!(std::ptr::null_mut())
1038 } else {
1039 let runs_before = runs_before.iter().map(|s| {
1040 let s = format!("{s}\0");
1041 quote!(#s.as_ptr() as *mut ::std::os::raw::c_char)
1042 });
1043 quote!(&[#(#runs_before),*, std::ptr::null_mut()] as *const *mut ::std::os::raw::c_char as *mut *mut ::std::os::raw::c_char)
1044 };
1045 let runs_after_output = if runs_after.is_empty() {
1046 quote!(std::ptr::null_mut())
1047 } else {
1048 let runs_after = runs_after.iter().map(|s| {
1049 let s = format!("{s}\0");
1050 quote!(#s.as_ptr() as *mut ::std::os::raw::c_char)
1051 });
1052 quote!(&[#(#runs_after),*, std::ptr::null_mut()] as *const *mut ::std::os::raw::c_char as *mut *mut ::std::os::raw::c_char)
1053 };
1054 let feature_data_type = feature_data_type
1055 .unwrap_or_else(|| syn::parse_str::<syn::Type>("()").expect("Unable to create identifier"));
1056
1057 let output = quote!(
1058 #[doc(hidden)]
1059 #[::vpp_plugin::macro_support::ctor::ctor(crate_path = ::vpp_plugin::macro_support::ctor)]
1060 fn #ctor_fn_ident() {
1061 unsafe { #ident.register(); }
1062 }
1063
1064 #[doc(hidden)]
1065 #[::vpp_plugin::macro_support::ctor::dtor(crate_path = ::vpp_plugin::macro_support::ctor)]
1066 fn #dtor_fn_ident() {
1067 unsafe { #ident.unregister(); }
1068 }
1069
1070 static #ident: ::vpp_plugin::vnet::feature::FeatureRegistration<#feature_data_type> = unsafe {
1071 ::vpp_plugin::vnet::feature::FeatureRegistration::new(
1072 ::vpp_plugin::bindings::vnet_feature_registration_t {
1073 arc_name: #arc_name_lit.as_ptr() as *mut ::std::os::raw::c_char,
1074 runs_before: #runs_before_output,
1075 runs_after: #runs_after_output,
1076 enable_disable_cb: None,
1077 ..::vpp_plugin::bindings::vnet_feature_registration_t::new()
1078 },
1079 &#reg_ident,
1080 )
1081 };
1082 );
1083
1084 output.into()
1087}
1088
1089#[proc_macro_attribute]
1109pub fn vlib_cli_command(attributes: TokenStream, function: TokenStream) -> TokenStream {
1110 let mut path: Option<syn::LitStr> = None;
1111 let mut short_help: Option<syn::LitStr> = None;
1112 let attr_parser = syn::meta::parser(|meta| {
1113 if meta.path.is_ident("path") {
1114 path = Some(meta.value()?.parse()?);
1115 Ok(())
1116 } else if meta.path.is_ident("short_help") {
1117 short_help = Some(meta.value()?.parse()?);
1118 Ok(())
1119 } else {
1120 Err(meta.error("unsupported vlib_cli_command property"))
1121 }
1122 });
1123
1124 syn::parse_macro_input!(attributes with attr_parser);
1125
1126 let item: syn::Item = syn::parse_macro_input!(function);
1127 if let syn::Item::Fn(function) = item {
1128 let syn::ItemFn {
1129 attrs,
1130 block,
1131 vis,
1132 sig:
1133 syn::Signature {
1134 ident,
1135 unsafety,
1136 constness,
1137 abi,
1138 inputs,
1139 output,
1140 ..
1141 },
1142 ..
1143 } = function;
1144
1145 let path = path.expect(
1146 "Missing attribute 'path'. Example: #[vlib_cli_command(path = \"rust-example\")]",
1147 );
1148 let path = format!("{}\0", path.value());
1149 let short_help = if let Some(short_help) = short_help {
1150 let short_help = format!("{}\0", short_help.value());
1151 quote!(#short_help.as_ptr() as *mut ::std::os::raw::c_char)
1152 } else {
1153 quote!(std::ptr::null_mut())
1154 };
1155 let raw_fn_ident = syn::parse_str::<syn::Ident>(format!("__{}", ident).as_ref())
1156 .expect("Unable to create identifier");
1157 let ctor_fn_ident = syn::parse_str::<syn::Ident>(
1158 format!("__vlib_cli_command_registration_{}", ident).as_ref(),
1159 )
1160 .expect("Unable to create identifier");
1161 let dtor_fn_ident = syn::parse_str::<syn::Ident>(
1162 format!("__vlib_cli_command_unregistration_{}", ident).as_ref(),
1163 )
1164 .expect("Unable to create identifier");
1165 let cli_command_ident =
1166 syn::parse_str::<syn::Ident>(format!("_VLIB_CLI_COMMAND_{}", ident).as_ref())
1167 .expect("Unable to create identifier");
1168
1169 let output = quote!(
1170 #(#attrs)*
1171 #vis #unsafety #abi #constness fn #ident(#inputs) #output #block
1172
1173 #[doc(hidden)]
1174 unsafe extern "C" fn #raw_fn_ident(
1175 vm: *mut ::vpp_plugin::bindings::vlib_main_t,
1176 input: *mut ::vpp_plugin::bindings::unformat_input_t,
1177 _cmd: *mut ::vpp_plugin::bindings::vlib_cli_command_t,
1178 ) -> *mut ::vpp_plugin::bindings::clib_error_t
1179 {
1180 let s = ::vpp_plugin::vppinfra::unformat::raw_unformat_line_input_to_string(input);
1181 if let Err(e) = #ident(::vpp_plugin::vlib::BarrierHeldMainRef::from_ptr_mut(vm), s.as_str()) {
1182 e.into_raw()
1183 } else {
1184 std::ptr::null_mut()
1185 }
1186 }
1187
1188 static #cli_command_ident: ::vpp_plugin::vlib::cli::CommandRegistration = ::vpp_plugin::vlib::cli::CommandRegistration::new(::vpp_plugin::bindings::vlib_cli_command_t {
1189 path: #path.as_ptr() as *mut ::std::os::raw::c_char,
1190 short_help: #short_help,
1191 long_help: std::ptr::null_mut(), function: Some(#raw_fn_ident),
1193 is_mp_safe: 0,
1194 ..::vpp_plugin::bindings::vlib_cli_command_t::new()
1195 });
1196
1197 #[::vpp_plugin::macro_support::ctor::ctor(crate_path = ::vpp_plugin::macro_support::ctor)]
1198 unsafe fn #ctor_fn_ident () {
1199 unsafe {
1200 #cli_command_ident.register();
1201 }
1202 }
1203
1204 #[::vpp_plugin::macro_support::ctor::dtor(crate_path = ::vpp_plugin::macro_support::ctor)]
1205 unsafe fn #dtor_fn_ident() {
1206 unsafe {
1207 #cli_command_ident.unregister();
1208 }
1209 }
1210 );
1211
1212 output.into()
1215 } else {
1216 panic!("#[vlib_cli_command] items must be functions");
1217 }
1218}