Skip to main content

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        #[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    // eprintln!("{}", output);
119
120    output.into()
121}
122
123/// Marks a function as an VPP plugin init function.
124///
125/// Multiple init functions are supported, but the invocation order is not guaranteed.
126///
127/// # Examples
128///
129/// ```
130/// # use vpp_plugin::{vlib::BarrierHeldMainRef, vlib_init_function, vppinfra::error::ErrorStack};
131///
132/// #[vlib_init_function]
133/// fn foo(_vm: &mut BarrierHeldMainRef) -> Result<(), ErrorStack> {
134///   println!("Hello, world!");
135///   Ok(())
136/// }
137/// ```
138#[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        // Ensure that visibility modifier is not present
160        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        // eprintln!("{}", output);
239
240        output.into()
241    } else {
242        panic!("#[vlib_init_function] items must be functions");
243    }
244}
245
246/// Derives the NextNodes trait for a VPP next node enum
247///
248/// Only unit variants are allowed and they must not have explicit values.
249///
250/// # Attributes
251///
252/// - Each variant must have a `#[next_node = "<node-name>"]` attribute, where `<node-name>` is
253///   the name of a VPP node.
254///
255/// # Examples
256///
257/// ```
258/// # use vpp_plugin::NextNodes;
259/// #[derive(NextNodes)]
260/// enum ExampleNextNode {
261///     #[next_node = "drop"]
262///    Drop,
263/// }
264/// ```
265#[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        // eprintln!("{}", output);
316
317        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/// Derives the ErrorCounters trait for a VPP error counter enum
387///
388/// Only unit variants are allowed and they must not have explicit values.
389///
390/// # Attributes
391///
392/// Each variant must have a `#[error_counter(...)]` attribute, with the following key-value pairs:
393/// - `name = "<name>"`: (optional) The name of the error counter. If not provided, the variant name will be used.
394/// - `description = "<description>"`: (required) A description of the error counter
395/// - `severity = <severity>`: (required) The severity of the error counter. Must be one of
396///   `INFO`, `WARNING`, `ERROR`, or `CRITICAL`.
397///
398/// # Examples
399///
400/// ```
401/// # use vpp_plugin::ErrorCounters;
402/// #[derive(ErrorCounters)]
403/// enum ExampleErrors {
404///     #[error_counter(name = "drop", description = "Example drop", severity = ERROR)]
405///     Drop,
406/// }
407/// ```
408#[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        // eprintln!("{}", output);
482
483        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    // Features equivalent to "v8.2a,sha2,aes", but avoiding use of unstable v8.2a feature
495    (
496        "octeontx2",
497        Some("aarch64"),
498        Some("crc,lse,rdm,pan,lor,vh,ras,dpb,sha2,aes"),
499    ),
500    // Features equivalent to "v8.1a,sha2,aes", but avoiding use of unstable v8.1a feature
501    (
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    // Features equivalent to "v8.2a,sha2,aes", but avoiding use of unstable v8.2a feature
508    (
509        "neoversen1",
510        Some("aarch64"),
511        Some("crc,lse,rdm,pan,lor,vh,ras,dpb,sha2,aes"),
512    ),
513    // Features equivalent to "v9a,sha2,aes", but avoiding use of unstable v9a feature
514    (
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/// Registers a VPP node and associated function
561///
562/// This registers an internal VPP node, which is the most common type of node (as opposed to
563/// processing or input nodes).
564///
565/// In addition, node functions are also registered, compiled for multiple CPU architectures and
566/// target features to allow VPP to select the optimal implementation for the current CPU. This
567/// only has an effect for code inlined into the node functions. In other words, `Node::function`
568/// should be marked as `#[inline(always)]` and any other functions directly or indirectly called
569/// in performance-critical paths should be similarly marked.
570///
571/// # Attributes
572///
573/// The macro takes key-value attributes as follows:
574/// - `name`: (required, string literal) The name of the VPP node.
575/// - `instance`: (required, ident) The instance of the node.
576/// - `runtime_data_default`: (optional, ident) An identifier for a constant value of type
577///   `Node::RuntimeData` to use as the default runtime data for this node.
578/// - `format_trace`: (optional, ident) An identifier for a function with the signature
579///   `fn(&mut MainRef, &mut NodeRef<...>, &TraceData) -> String` to format trace
580///   data for this node.
581///
582/// # Examples
583///
584/// ```
585/// # use std::fmt;
586/// # use vpp_plugin::{vlib::{self, node::Node}, vlib_node, ErrorCounters, NextNodes};
587/// #[derive(Copy, Clone)]
588/// struct ExampleTrace;
589///
590/// impl fmt::Display for ExampleTrace {
591///     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
592///         Ok(())
593///     }
594/// }
595///
596/// fn format_example_trace(
597///     _vm: &mut vlib::MainRef,
598///     _node: &mut vlib::NodeRef<ExampleNode>,
599///     t: &ExampleTrace,
600/// ) -> String {
601///     t.to_string()
602/// }
603/// # #[derive(NextNodes)]
604/// # enum ExampleNextNode {
605/// #     #[next_node = "drop"]
606/// #    _Drop,
607/// # }
608/// # #[derive(ErrorCounters)]
609/// # enum ExampleErrorCounter {
610/// #     #[error_counter(description = "Drops", severity = ERROR)]
611/// #     _Drop,
612/// # }
613///
614/// static EXAMPLE_NODE: ExampleNode = ExampleNode::new();
615///
616/// #[vlib_node(
617///     name = "example",
618///     instance = EXAMPLE_NODE,
619///     format_trace = format_example_trace,
620/// )]
621/// struct ExampleNode;
622///
623/// impl ExampleNode {
624///     const fn new() -> Self {
625///         Self
626///     }
627/// }
628///
629/// impl vlib::node::Node for ExampleNode {
630///     type Vector = ();
631///     type Scalar = ();
632///     type Aux = ();
633///
634///     type NextNodes = ExampleNextNode;
635///     type RuntimeData = ();
636///     type TraceData = ExampleTrace;
637///     type Errors = ExampleErrorCounter;
638///     type FeatureData = ();
639///
640///     #[inline(always)]
641///     unsafe fn function(
642///         &self,
643///         _vm: &mut vlib::MainRef,
644///         _node: &mut vlib::NodeRuntimeRef<ExampleNode>,
645///         _frame: &mut vlib::FrameRef<Self>,
646///     ) -> u16 {
647///         todo!()
648///     }
649/// }
650/// ```
651#[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                // It would be ideal to use #runtime_data::default() here, but we cannot call that in a const context and we need to here
714                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        // eprintln!("{}", output);
854
855        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/// Registers a VPP feature
938///
939/// Allowing the VPP node to be executed in a feature arc. Note that the feature won't be
940/// executed until enabled.
941///
942/// # Attributes
943///
944/// Each variant must have a `#[error_counter(...)]` attribute, with the following key-value pairs:
945/// - `identifier = <ident>`: (required) An identifier of a static of type
946///   `vpp_plugin::vnet::feature::FeatureRegistration` that will be declared and registered by
947///   the macro.
948/// - `arc_name = "<name>"`: (required, string literal) The name of the feature arc the node will
949///   be registered to.
950/// - `node = <ident>`: (required, string literal) The name of a node type registered using
951///   [`vlib_node`].
952/// - `runs_before = [("<feature-name>")*]`: (optional, string literal) A list of features that
953///   should be executed in the feature arc before this feature is executed.
954/// - `runs_after = [("<feature-name>")*]`: (optional, string literal) A list of features that
955///   should be executed in the feature arc after this feature is executed.
956///
957/// # Examples
958///
959/// ```
960/// # use vpp_plugin::{vlib::{self, node::Node}, vlib_node, vnet_feature_init, ErrorCounters, NextNodes};
961/// # #[derive(NextNodes)]
962/// # enum ExampleNextNode {
963/// #     #[next_node = "drop"]
964/// #    _Drop,
965/// # }
966/// # #[derive(ErrorCounters)]
967/// # enum ExampleErrorCounter {}
968/// #
969/// # static EXAMPLE_NODE: ExampleNode = ExampleNode::new();
970/// #
971/// #[vlib_node(
972///     name = "example",
973///     instance = EXAMPLE_NODE,
974/// )]
975/// struct ExampleNode;
976///
977/// // ...
978///
979/// # impl ExampleNode {
980/// #     const fn new() -> Self {
981/// #         Self
982/// #     }
983/// # }
984/// # impl vlib::node::Node for ExampleNode {
985/// #     type Vector = ();
986/// #     type Scalar = ();
987/// #     type Aux = ();
988/// #
989/// #     type NextNodes = ExampleNextNode;
990/// #     type RuntimeData = ();
991/// #     type TraceData = ();
992/// #     type Errors = ExampleErrorCounter;
993/// #     type FeatureData = ();
994/// #
995/// #     #[inline(always)]
996/// #     unsafe fn function(
997/// #         &self,
998/// #         _vm: &mut vlib::MainRef,
999/// #         _node: &mut vlib::NodeRuntimeRef<Self>,
1000/// #         _frame: &mut vlib::FrameRef<Self>,
1001/// #     ) -> u16 {
1002/// #         todo!()
1003/// #     }
1004/// # }
1005/// vnet_feature_init! {
1006///     identifier: EXAMPLE_FEAT,
1007///     arc_name: "ip4-unicast",
1008///     node: ExampleNode,
1009///     runs_before: ["ip4-flow-classify"],
1010///     runs_after: ["ip4-sv-reassembly-feature"],
1011/// }
1012/// ```
1013#[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    // eprintln!("{}", output);
1085
1086    output.into()
1087}
1088
1089/// Creates a CLI command function
1090///
1091/// # Attributes
1092///
1093/// - `path`: (required, string literal) The CLI command path.
1094/// - `short_help`: (optional, string literal) A short help string for the CLI command.
1095///
1096/// # Examples
1097///
1098/// ```
1099/// # use vpp_plugin::{vlib_cli_command, vlib, vppinfra::error::ErrorStack};
1100/// #[vlib_cli_command(
1101///     path = "rust-example",
1102///     short_help = "rust-example <interface-name> [disable]",
1103/// )]
1104/// fn enable_disable_command(_vm: &mut vlib::BarrierHeldMainRef, _input: &str) -> Result<(), ErrorStack> {
1105///     Ok(())
1106/// }
1107/// ```
1108#[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(), // TODO
1192                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        // eprintln!("{}", output);
1213
1214        output.into()
1215    } else {
1216        panic!("#[vlib_cli_command] items must be functions");
1217    }
1218}