rust: EnumString derive accepts a enum_string_style parameter

So that http2.frametype and http2.errorcode can use EnumString
without changing the format, as they used UPPERCASE instead of
the default EnumString snake_case
pull/14025/head
Philippe Antoine 3 weeks ago committed by Victor Julien
parent 0b63894c5d
commit 454f73b445

@ -59,7 +59,7 @@ pub fn derive_app_layer_state(input: TokenStream) -> TokenStream {
applayerstate::derive_app_layer_state(input)
}
#[proc_macro_derive(EnumStringU8, attributes(name))]
#[proc_macro_derive(EnumStringU8, attributes(name, suricata))]
pub fn derive_enum_string_u8(input: TokenStream) -> TokenStream {
stringenum::derive_enum_string::<u8>(input, "u8")
}
@ -69,7 +69,7 @@ pub fn derive_enum_string_u16(input: TokenStream) -> TokenStream {
stringenum::derive_enum_string::<u16>(input, "u16")
}
#[proc_macro_derive(EnumStringU32, attributes(name))]
#[proc_macro_derive(EnumStringU32, attributes(name, suricata))]
pub fn derive_enum_string_u32(input: TokenStream) -> TokenStream {
stringenum::derive_enum_string::<u32>(input, "u32")
}

@ -15,14 +15,75 @@
* 02110-1301, USA.
*/
//! String-enumeration derive macro
/*
String enumeration allows a compact representation, and ensure that
there is no discrepancy between what is logged, and what can be parser
in signature keywords.
Usage: just derive EnumStringUX with X being the number of bits used in repr
#[derive(EnumStringU8)]
#[repr(u8)]
pub enum ExampleEnum {
Zero = 0,
BestValueEver = 42,
}
Then, get 4 functions to translate from u8 <-> ExampleEnum <-> string
- from_u : takes an unsigned and return an Option<ExampleEnum>
- into_u : takes an ExampleEnum and return an unsigned
- from_str : takes a string and return an Option<ExampleEnum>
- into_str : takes an ExampleEnum and return a string (&'static str)
Case:
snake_case is used by default:
ExampleEnum::BestValueEver.to_str() is "best_value_ever"
Strings parsed to get an enumeration value are case insensitive
Parameters:
The enum can have a suricata attribute, which is a list of named values
- enum_string_style can be:
- UPPERCASE : uppercase is used for logging and parsing
- LOG_UPPERCASE : uppercase is only used for logging, snake_case for parsing
With UPPERCASE
ExampleEnum::BestValueEver.to_str() is "BESTVALUEEVER" (without underscores)
*/
extern crate proc_macro;
use super::applayerevent::transform_name;
use proc_macro::TokenStream;
use quote::quote;
use syn::{self, parse_macro_input, DeriveInput};
use std::str::FromStr;
use syn::{self, parse_macro_input, DeriveInput};
fn get_attr_enum_string_style(attr: &syn::Attribute) -> String {
let meta = attr.parse_meta().unwrap();
if let syn::Meta::List(l) = meta {
for n in l.nested {
if let syn::NestedMeta::Meta(syn::Meta::NameValue(nv)) = n {
if nv.path.is_ident("enum_string_style") {
if let syn::Lit::Str(s) = nv.lit {
return s.value();
}
panic!("enum_string_style invalid syntax");
}
}
}
panic!("no enum_string_style");
}
panic!("suricata attribute is not a list");
}
pub fn derive_enum_string<T: std::str::FromStr + quote::ToTokens>(input: TokenStream, ustr: &str) -> TokenStream where <T as FromStr>::Err: std::fmt::Display {
pub fn derive_enum_string<T: std::str::FromStr + quote::ToTokens>(
input: TokenStream, ustr: &str,
) -> TokenStream
where
<T as FromStr>::Err: std::fmt::Display,
{
let input = parse_macro_input!(input as DeriveInput);
let name = input.ident;
let mut values = Vec::new();
@ -30,13 +91,37 @@ pub fn derive_enum_string<T: std::str::FromStr + quote::ToTokens>(input: TokenSt
let mut names_upper = Vec::new();
let mut fields = Vec::new();
let mut enum_string_style = String::from("");
if let syn::Data::Enum(ref data) = input.data {
for attr in input.attrs.iter() {
if attr.path.is_ident("suricata") {
enum_string_style = get_attr_enum_string_style(attr);
}
}
for v in (&data.variants).into_iter() {
if let Some((_, val)) = &v.discriminant {
let fname = transform_name(&v.ident.to_string());
let fnameu = fname.to_ascii_uppercase();
names.push(fname);
names_upper.push(fnameu);
match &enum_string_style[..] {
"UPPERCASE" => {
names.push(v.ident.to_string().to_ascii_uppercase());
names_upper.push(v.ident.to_string().to_ascii_uppercase());
}
"LOG_UPPERCASE" => {
names.push(v.ident.to_string().to_ascii_uppercase());
let fname = transform_name(&v.ident.to_string());
let fnameu = fname.to_ascii_uppercase();
names_upper.push(fnameu);
}
"" => { // snake_case
let fname = transform_name(&v.ident.to_string());
let fnameu = fname.to_ascii_uppercase();
names.push(fname);
names_upper.push(fnameu);
}
_ => {
panic!("EnumString style unknown {}", enum_string_style);
}
};
fields.push(v.ident.clone());
if let syn::Expr::Lit(l) = val {
if let syn::Lit::Int(li) = &l.lit {
@ -59,7 +144,9 @@ pub fn derive_enum_string<T: std::str::FromStr + quote::ToTokens>(input: TokenSt
panic!("EnumString can only be derived for enums");
}
let is_suricata = std::env::var("CARGO_PKG_NAME").map(|var| var == "suricata").unwrap_or(false);
let is_suricata = std::env::var("CARGO_PKG_NAME")
.map(|var| var == "suricata")
.unwrap_or(false);
let crate_id = if is_suricata {
syn::Ident::new("crate", proc_macro2::Span::call_site())
} else {

@ -19,23 +19,23 @@
pub mod byte_extract;
pub mod byte_math;
pub mod datasets;
pub mod entropy;
pub mod error;
pub mod float;
pub mod flow;
pub mod iprep;
pub mod parser;
pub mod requires;
pub mod stream_size;
pub mod tojson;
pub mod transforms;
pub mod uint;
pub mod float;
pub mod uri;
pub mod tojson;
pub mod vlan;
pub mod datasets;
use std::os::raw::c_int;
use std::ffi::CString;
use std::os::raw::c_int;
use suricata_sys::sys::{
DetectEngineCtx, SCDetectHelperKeywordRegister, SCDetectHelperKeywordSetCleanCString,
@ -46,7 +46,9 @@ use suricata_sys::sys::{
/// derive StringEnum.
pub trait EnumString<T> {
/// Return the enum variant of the given numeric value.
fn from_u(v: T) -> Option<Self> where Self: Sized;
fn from_u(v: T) -> Option<Self>
where
Self: Sized;
/// Convert the enum variant to the numeric value.
fn into_u(self) -> T;
@ -55,7 +57,9 @@ pub trait EnumString<T> {
fn to_str(&self) -> &'static str;
/// Get an enum variant from parsing a string.
fn from_str(s: &str) -> Option<Self> where Self: Sized;
fn from_str(s: &str) -> Option<Self>
where
Self: Sized;
}
/// Rust app-layer light version of SigTableElmt for simple sticky buffer
@ -138,7 +142,6 @@ pub const SIGMATCH_INFO_MULTI_UINT: u32 = 0x80000; // BIT_U32(19)
pub const SIGMATCH_INFO_ENUM_UINT: u32 = 0x100000; // BIT_U32(20)
pub const SIGMATCH_INFO_BITFLAGS_UINT: u32 = 0x200000; // BIT_U32(21)
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
// endian <big|little|dce>
@ -201,6 +204,56 @@ mod test {
assert_eq!(TestEnum::BestValueEver.to_str(), "best_value_ever");
assert_eq!(TestEnum::from_str("zero"), Some(TestEnum::Zero));
assert_eq!(TestEnum::from_str("nope"), None);
assert_eq!(TestEnum::from_str("best_value_ever"), Some(TestEnum::BestValueEver));
assert_eq!(
TestEnum::from_str("best_value_ever"),
Some(TestEnum::BestValueEver)
);
}
#[derive(Clone, Debug, PartialEq, EnumStringU8)]
#[repr(u8)]
#[suricata(enum_string_style = "LOG_UPPERCASE")]
pub enum TestEnumLogUppercase {
Zero = 0,
BestValueEver = 42,
}
#[test]
fn test_enum_string_log_uppercase() {
assert_eq!(TestEnumLogUppercase::Zero.to_str(), "ZERO");
assert_eq!(
TestEnumLogUppercase::BestValueEver.to_str(),
"BESTVALUEEVER"
);
assert_eq!(
TestEnumLogUppercase::from_str("zero"),
Some(TestEnumLogUppercase::Zero)
);
assert_eq!(
TestEnumLogUppercase::from_str("BEST_VALUE_EVER"),
Some(TestEnumLogUppercase::BestValueEver)
);
}
#[derive(Clone, Debug, PartialEq, EnumStringU8)]
#[repr(u8)]
#[suricata(enum_string_style = "UPPERCASE")]
pub enum TestEnumUppercase {
Zero = 0,
BestValueEver = 42,
}
#[test]
fn test_enum_string_uppercase() {
assert_eq!(TestEnumUppercase::Zero.to_str(), "ZERO");
assert_eq!(TestEnumUppercase::BestValueEver.to_str(), "BESTVALUEEVER");
assert_eq!(
TestEnumUppercase::from_str("zero"),
Some(TestEnumUppercase::Zero)
);
assert_eq!(
TestEnumUppercase::from_str("BESTVALUEEVER"),
Some(TestEnumUppercase::BestValueEver)
);
}
}

Loading…
Cancel
Save