vpp_plugin_macros/
lib.rs

1#![warn(
2    missing_docs,
3    missing_copy_implementations,
4    missing_debug_implementations
5)]
6
7//! Macros for writing VPP plugins in Rust
8//!
9//! This crate provides procedural macros to assist in writing VPP plugins in Rust.
10
11use 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/// Register the plugin so that it can be loaded by VPP
66///
67/// Must only be done once per shared object, or linker errors will result.
68///
69/// # Attributes
70///
71/// - `version`: (required, string literal) The version string of the plugin. Must be at most 63 characters.
72/// - `description`: (optional, string literal) A description of the plugin.
73///
74/// # Examples
75///
76/// ```
77/// # use vpp_plugin::vlib_plugin_register;
78/// vlib_plugin_register! {
79///     version: "1.0",
80///     description: "Example",
81/// }
82/// ```
83#[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    // Pad with zeroes to 64 bytes
97    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    // eprintln!("{}", output);
124
125    output.into()
126}
127
128/// Marks a function as an VPP plugin init function.
129///
130/// Multiple init functions are supported, but the invocation order is not guaranteed.
131///
132/// # Examples
133///
134/// ```
135/// # use vpp_plugin::{vlib::BarrierHeldMainRef, vlib_init_function, vppinfra::error::ErrorStack};
136///
137/// #[vlib_init_function]
138/// fn foo(_vm: &mut BarrierHeldMainRef) -> Result<(), ErrorStack> {
139///   println!("Hello, world!");
140///   Ok(())
141/// }
142/// ```
143#[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        // Ensure that visibility modifier is not present
165        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        // eprintln!("{}", output);
244
245        output.into()
246    } else {
247        panic!("#[vlib_init_function] items must be functions");
248    }
249}
250
251/// Derives the NextNodes trait for a VPP next node enum
252///
253/// Only unit variants are allowed and they must not have explicit values.
254///
255/// # Attributes
256///
257/// - Each variant must have a `#[next_node = "<node-name>"]` attribute, where `<node-name>` is
258///   the name of a VPP node.
259///
260/// # Examples
261///
262/// ```
263/// # use vpp_plugin::NextNodes;
264/// #[derive(NextNodes)]
265/// enum ExampleNextNode {
266///     #[next_node = "drop"]
267///    Drop,
268/// }
269/// ```
270#[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        // eprintln!("{}", output);
321
322        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/// Derives the ErrorCounters trait for a VPP error counter enum
392///
393/// Only unit variants are allowed and they must not have explicit values.
394///
395/// # Attributes
396///
397/// Each variant must have a `#[error_counter(...)]` attribute, with the following key-value pairs:
398/// - `name = "<name>"`: (optional) The name of the error counter. If not provided, the variant name will be used.
399/// - `description = "<description>"`: (required) A description of the error counter
400/// - `severity = <severity>`: (required) The severity of the error counter. Must be one of
401///   `INFO`, `WARNING`, `ERROR`, or `CRITICAL`.
402///
403/// # Examples
404///
405/// ```
406/// # use vpp_plugin::ErrorCounters;
407/// #[derive(ErrorCounters)]
408/// enum ExampleErrors {
409///     #[error_counter(name = "drop", description = "Example drop", severity = ERROR)]
410///     Drop,
411/// }
412/// ```
413#[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        // eprintln!("{}", output);
487
488        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/// Registers a VPP node and associated function
539///
540/// This registers an internal VPP node, which is the most common type of node (as opposed to
541/// processing or input nodes).
542///
543/// In addition, node functions are also registered, compiled for multiple CPU architectures and
544/// target features to allow VPP to select the optimal implementation for the current CPU. This
545/// only has an effect for code inlined into the node functions. In other words, `Node::function`
546/// should be marked as `#[inline(always)]` and any other functions directly or indirectly called
547/// in performance-critical paths should be similarly marked.
548///
549/// # Attributes
550///
551/// The macro takes key-value attributes as follows:
552/// - `name`: (required, string literal) The name of the VPP node.
553/// - `instance`: (required, ident) The instance of the node.
554/// - `runtime_data_default`: (optional, ident) An identifier for a constant value of type
555///   `Node::RuntimeData` to use as the default runtime data for this node.
556/// - `format_trace`: (optional, ident) An identifier for a function with the signature
557///   `fn(&mut MainRef, &mut NodeRef<...>, &TraceData) -> String` to format trace
558///   data for this node.
559///
560/// # Examples
561///
562/// ```
563/// # use std::fmt;
564/// # use vpp_plugin::{vlib::{self, node::Node}, vlib_node, ErrorCounters, NextNodes};
565/// #[derive(Copy, Clone)]
566/// struct ExampleTrace;
567///
568/// impl fmt::Display for ExampleTrace {
569///     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
570///         Ok(())
571///     }
572/// }
573///
574/// fn format_example_trace(
575///     _vm: &mut vlib::MainRef,
576///     _node: &mut vlib::NodeRef<ExampleNode>,
577///     t: &ExampleTrace,
578/// ) -> String {
579///     t.to_string()
580/// }
581/// # #[derive(NextNodes)]
582/// # enum ExampleNextNode {
583/// #     #[next_node = "drop"]
584/// #    _Drop,
585/// # }
586/// # #[derive(ErrorCounters)]
587/// # enum ExampleErrorCounter {
588/// #     #[error_counter(description = "Drops", severity = ERROR)]
589/// #     _Drop,
590/// # }
591///
592/// static EXAMPLE_NODE: ExampleNode = ExampleNode::new();
593///
594/// #[vlib_node(
595///     name = "example",
596///     instance = EXAMPLE_NODE,
597///     format_trace = format_example_trace,
598/// )]
599/// struct ExampleNode;
600///
601/// impl ExampleNode {
602///     const fn new() -> Self {
603///         Self
604///     }
605/// }
606///
607/// impl vlib::node::Node for ExampleNode {
608///     type Vector = ();
609///     type Scalar = ();
610///     type Aux = ();
611///
612///     type NextNodes = ExampleNextNode;
613///     type RuntimeData = ();
614///     type TraceData = ExampleTrace;
615///     type Errors = ExampleErrorCounter;
616///     type FeatureData = ();
617///
618///     #[inline(always)]
619///     unsafe fn function(
620///         &self,
621///         _vm: &mut vlib::MainRef,
622///         _node: &mut vlib::NodeRuntimeRef<ExampleNode>,
623///         _frame: &mut vlib::FrameRef<Self>,
624///     ) -> u16 {
625///         todo!()
626///     }
627/// }
628/// ```
629#[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                // It would be ideal to use #runtime_data::default() here, but we cannot call that in a const context and we need to here
690                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        // eprintln!("{}", output);
830
831        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/// Registers a VPP feature
914///
915/// Allowing the VPP node to be executed in a feature arc. Note that the feature won't be
916/// executed until enabled.
917///
918/// # Attributes
919///
920/// Each variant must have a `#[error_counter(...)]` attribute, with the following key-value pairs:
921/// - `identifier = <ident>`: (required) An identifier of a static of type
922///   `vpp_plugin::vnet::feature::FeatureRegistration` that will be declared and registered by
923///   the macro.
924/// - `arc_name = "<name>"`: (required, string literal) The name of the feature arc the node will
925///   be registered to.
926/// - `node = <ident>`: (required, string literal) The name of a node type registered using
927///   [`vlib_node`].
928/// - `runs_before = [("<feature-name>")*]`: (optional, string literal) A list of features that
929///   should be executed in the feature arc before this feature is executed.
930/// - `runs_after = [("<feature-name>")*]`: (optional, string literal) A list of features that
931///   should be executed in the feature arc after this feature is executed.
932///
933/// # Examples
934///
935/// ```
936/// # use vpp_plugin::{vlib::{self, node::Node}, vlib_node, vnet_feature_init, ErrorCounters, NextNodes};
937/// # #[derive(NextNodes)]
938/// # enum ExampleNextNode {
939/// #     #[next_node = "drop"]
940/// #    _Drop,
941/// # }
942/// # #[derive(ErrorCounters)]
943/// # enum ExampleErrorCounter {}
944/// #
945/// # static EXAMPLE_NODE: ExampleNode = ExampleNode::new();
946/// #
947/// #[vlib_node(
948///     name = "example",
949///     instance = EXAMPLE_NODE,
950/// )]
951/// struct ExampleNode;
952///
953/// // ...
954///
955/// # impl ExampleNode {
956/// #     const fn new() -> Self {
957/// #         Self
958/// #     }
959/// # }
960/// # impl vlib::node::Node for ExampleNode {
961/// #     type Vector = ();
962/// #     type Scalar = ();
963/// #     type Aux = ();
964/// #
965/// #     type NextNodes = ExampleNextNode;
966/// #     type RuntimeData = ();
967/// #     type TraceData = ();
968/// #     type Errors = ExampleErrorCounter;
969/// #     type FeatureData = ();
970/// #
971/// #     #[inline(always)]
972/// #     unsafe fn function(
973/// #         &self,
974/// #         _vm: &mut vlib::MainRef,
975/// #         _node: &mut vlib::NodeRuntimeRef<Self>,
976/// #         _frame: &mut vlib::FrameRef<Self>,
977/// #     ) -> u16 {
978/// #         todo!()
979/// #     }
980/// # }
981/// vnet_feature_init! {
982///     identifier: EXAMPLE_FEAT,
983///     arc_name: "ip4-unicast",
984///     node: ExampleNode,
985///     runs_before: ["ip4-flow-classify"],
986///     runs_after: ["ip4-sv-reassembly-feature"],
987/// }
988/// ```
989#[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    // eprintln!("{}", output);
1061
1062    output.into()
1063}
1064
1065/// Creates a CLI command function
1066///
1067/// # Attributes
1068///
1069/// - `path`: (required, string literal) The CLI command path.
1070/// - `short_help`: (optional, string literal) A short help string for the CLI command.
1071///
1072/// # Examples
1073///
1074/// ```
1075/// # use vpp_plugin::{vlib_cli_command, vlib, vppinfra::error::ErrorStack};
1076/// #[vlib_cli_command(
1077///     path = "rust-example",
1078///     short_help = "rust-example <interface-name> [disable]",
1079/// )]
1080/// fn enable_disable_command(_vm: &mut vlib::BarrierHeldMainRef, _input: &str) -> Result<(), ErrorStack> {
1081///     Ok(())
1082/// }
1083/// ```
1084#[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(), // TODO
1168                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        // eprintln!("{}", output);
1189
1190        output.into()
1191    } else {
1192        panic!("#[vlib_cli_command] items must be functions");
1193    }
1194}