#![deny(missing_docs)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::cognitive_complexity)]
#![allow(clippy::single_match)]
extern crate proc_macro;
mod errors;
mod well_known_types;
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
use std::iter::Extend;
use syn::spanned::Spanned;
use syn::{parse_quote, Error, Expr, ExprLit, FnArg, Ident, Lit, Token};
use uuid::Uuid;
use well_known_types::{WellKnownType, WellKnownTypes};
#[cfg(test)]
mod tests;
#[proc_macro_attribute]
pub fn trace_logging_provider(
attr: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let output = trace_logging_events_core(attr.into(), input.into());
output.into()
}
fn trace_logging_events_core(attr: TokenStream, item_tokens: TokenStream) -> TokenStream {
let mut errors: Vec<Error> = Vec::new();
let logging_trait: syn::ItemTrait = match syn::parse2(item_tokens) {
Err(e) => {
return e.to_compile_error();
}
Ok(syn::Item::Trait(t)) => t,
Ok(syn::Item::Mod(m)) => {
return Error::new_spanned(&m, "Modules are not yet supported, but thanks for asking.")
.to_compile_error();
}
Ok(unrecognized) => {
return Error::new_spanned(
&unrecognized,
"The #[trace_logging_provider] attribute cannot be used with this kind of item.",
)
.to_compile_error();
}
};
let provider_attrs: ProviderAttributes = match syn::parse2(attr) {
Ok(p) => p,
Err(e) => {
errors.push(e);
ProviderAttributes::default()
}
};
let provider_ident = &logging_trait.ident;
let provider_ident_string = provider_ident.to_string();
let wk = WellKnownTypes::new();
let mut output = TokenStream::new();
let provider_mod_ident: Ident = ident_suffix(provider_ident, "implementation");
let mut event_descriptors: Vec<syn::Item> = Vec::new();
let provider_metadata_ident = Ident::new(
&format!("{}_PROVIDER_METADATA", provider_ident_string),
provider_ident.span(),
);
{
let provider_name = provider_attrs
.provider_name
.as_ref()
.unwrap_or(&provider_ident_string);
output.extend(create_provider_metadata(
&provider_name,
&provider_metadata_ident,
));
}
let mut provider_impl_items = TokenStream::new();
for (method_index, method) in logging_trait
.items
.iter()
.filter_map(|item| {
if let syn::TraitItem::Method(m) = item {
Some(m)
} else {
None
}
})
.enumerate()
{
let event_id = method_index as u16;
if method.sig.asyncness.is_some() {
errors.push(Error::new_spanned(
method,
"Async event methods are not supported.",
));
}
if method.sig.unsafety.is_some() {
errors.push(Error::new_spanned(
method,
"Event methods should not be marked unsafe.",
));
}
if !method.sig.generics.params.is_empty() {
errors.push(Error::new_spanned(
method,
"Generic event methods are not supported.",
));
}
match &method.sig.output {
syn::ReturnType::Default => {}
_ => {
errors.push(Error::new_spanned(
method,
"Event methods must not return data.",
));
}
}
if let Some(block) = method.default.as_ref() {
errors.push(Error::new_spanned(
block,
"Event methods must not contain an implementation.",
));
}
let event_ident = &method.sig.ident;
let event_name: String = method.sig.ident.to_string();
let mut data_descriptor_array = TokenStream::new();
data_descriptor_array.extend(quote! {});
let mut event_metadata: Vec<Expr> = Vec::new();
event_metadata.push(parse_quote! { 0 });
event_metadata.push(parse_quote! { 0 });
event_metadata.push(parse_quote! { 0 });
append_utf8_str_chars(&mut event_metadata, &event_name);
let mut statements = TokenStream::new();
let mut found_receiver = false;
let mut sig = method.sig.clone();
for param in sig.inputs.iter_mut() {
let param_span = param.span();
match param {
FnArg::Receiver(_) => {
errors.push(Error::new_spanned(param, "Event methods should not provide any receiver arguments (&self, &mut self, etc.)."));
found_receiver = true;
}
FnArg::Typed(param_typed) => {
let mut event_attr: Option<syn::Attribute> = None;
param_typed.attrs.retain(|a| {
if a.path == parse_quote!(event) {
event_attr = Some(a.clone());
false
} else if a.path == parse_quote!(doc) {
true
} else {
errors.push(Error::new_spanned(
a,
"This attribute is not permitted on event fields.",
));
true
}
});
let param_name: &Ident = match &*param_typed.pat {
syn::Pat::Ident(ref name) => &name.ident,
_ => {
errors.push(Error::new(
param.span(),
"Only ordinary parameter patterns are supported on event methods.",
));
continue;
}
};
if parse_event_field(
&mut errors,
&wk,
event_attr.as_ref(),
param_span,
¶m_name,
&mut *param_typed.ty,
&mut data_descriptor_array,
&mut event_metadata,
&mut statements,
)
.is_err()
{
errors.push(Error::new_spanned(
¶m,
"This type is not supported for event parameters.",
));
}
}
}
}
if !found_receiver {
sig.inputs.insert(0, parse_quote!(&self));
}
sig.inputs.insert(
1,
parse_quote!(options: core::option::Option<&::win_etw_provider::EventOptions>),
);
let event_metadata_len = event_metadata.len();
if event_metadata_len > 0xffff {
errors.push(Error::new(
method.span(),
"Event metadata is too large to encode; reduce the complexity of this event.",
));
continue;
}
let event_metadata_len_b0 = (event_metadata_len & 0xff) as u8;
let event_metadata_len_b1 = (event_metadata_len >> 8) as u8;
event_metadata[0] = parse_quote! { #event_metadata_len_b0 };
event_metadata[1] = parse_quote! { #event_metadata_len_b1 };
let event_attrs = parse_event_attributes(&mut errors, &method.sig.ident, &method.attrs);
let event_level = event_attrs.level;
let event_opcode = event_attrs.opcode;
let event_task = event_attrs.task;
event_descriptors.push(parse_quote!{
pub(crate) static #event_ident: ::win_etw_provider::EventDescriptor = ::win_etw_provider::EventDescriptor {
id: #event_id,
version: 0,
channel: 11,
level: #event_level,
opcode: #event_opcode,
task: #event_task,
keyword: 0,
};
});
let event_attrs_method_attrs = &event_attrs.method_attrs;
let event_is_enabled_name = Ident::new(
&format!("{}_is_enabled", method.sig.ident.to_string()),
method.sig.ident.span(),
);
provider_impl_items.extend(quote!{
#( #event_attrs_method_attrs )*
#[cfg_attr(not(target_os = "windows"), allow(unused_variables))]
pub #sig
{
#[cfg(target_os = "windows")]
{
use ::win_etw_provider::EventDataDescriptor;
#[link_section = ".rdata$etw1"]
#[used]
static EVENT_METADATA: [u8; #event_metadata_len] = [ #( #event_metadata, )* ];
let mut event_descriptor: ::win_etw_provider::EventDescriptor = ::win_etw_provider::EventDescriptor {
id: #event_id,
version: 0,
channel: 11,
level: #event_level,
opcode: #event_opcode,
task: #event_task,
keyword: 0,
};
if let Some(opts) = options {
if let Some(level) = opts.level {
event_descriptor.level = level;
}
}
#statements
let data_descriptors = [
EventDataDescriptor::for_provider_metadata(&#provider_metadata_ident[..]),
EventDataDescriptor::for_event_metadata(&EVENT_METADATA[..]),
#data_descriptor_array
];
::win_etw_provider::Provider::write(&self.provider,
options,
&event_descriptor,
&data_descriptors,
);
}
}
pub fn #event_is_enabled_name(&self, level: ::core::option::Option<::win_etw_provider::Level>) -> bool {
#[cfg(target_os = "windows")]
{
let mut event_descriptor: ::win_etw_provider::EventDescriptor = ::win_etw_provider::EventDescriptor {
id: #event_id,
version: 0,
channel: 11,
level: level.unwrap_or(#event_level),
opcode: #event_opcode,
task: #event_task,
keyword: 0,
};
::win_etw_provider::Provider::is_event_enabled(
&self.provider,
&event_descriptor)
}
#[cfg(not(target_os = "windows"))]
{
false
}
}
});
}
let vis = logging_trait.vis.clone();
let provider_guid_const = uuid_to_expr(&provider_attrs.uuid);
let doc_path: syn::Path = parse_quote!(doc);
let provider_attrs = logging_trait
.attrs
.iter()
.filter(|a| a.path == doc_path)
.collect::<Vec<_>>();
output.extend(quote! {
#( #provider_attrs )*
#vis struct #provider_ident {
provider: ::core::option::Option<::win_etw_provider::EtwProvider>,
}
impl #provider_ident {
pub fn new() -> Self {
let provider = match ::win_etw_provider::EtwProvider::new(&Self::PROVIDER_GUID) {
Ok(mut provider) => {
#[cfg(target_os = "windows")]
{
let _ = provider.register_provider_metadata(&#provider_metadata_ident);
}
Some(provider)
}
Err(_) => None,
};
Self { provider }
}
pub fn new_err() -> ::core::result::Result<Self, ::win_etw_provider::Error> {
Ok(Self {
provider: Some(::win_etw_provider::EtwProvider::new(&Self::PROVIDER_GUID)?),
})
}
pub fn null() -> Self {
Self { provider: None }
}
#[allow(unused_variable)]
pub const PROVIDER_GUID: ::win_etw_provider::GUID = #provider_guid_const;
}
#[allow(non_snake_case)]
impl #provider_ident {
#provider_impl_items
}
#[doc(hidden)]
#[allow(non_snake_case)]
mod #provider_mod_ident {
}
});
output.extend(errors.into_iter().map(|e| e.to_compile_error()));
output
}
fn err_spanned<T: quote::ToTokens>(item: &T, msg: &str) -> TokenStream {
Error::new_spanned(item, msg).to_compile_error()
}
fn create_provider_metadata(provider_name: &str, provider_metadata_ident: &Ident) -> TokenStream {
let mut provider_metadata: Vec<u8> = Vec::new();
let provider_metadata_len = 2 + provider_name.len() + 1;
if provider_metadata_len > 0xffff {
return err_spanned(&provider_name, "The provider name is too long.");
}
provider_metadata.push((provider_metadata_len & 0xff) as u8);
provider_metadata.push((provider_metadata_len >> 8) as u8);
provider_metadata.extend_from_slice(provider_name.as_bytes());
provider_metadata.push(0);
quote! {
#[link_section = ".rdata$etw2"]
#[used]
#[allow(non_upper_case_globals)]
#[cfg(target_os = "windows")]
static #provider_metadata_ident: [u8; #provider_metadata_len] = [
#(
#provider_metadata,
)*
];
}
}
fn append_utf8_str_chars(output: &mut Vec<Expr>, s: &str) {
for &b in s.as_bytes().iter() {
output.push(parse_quote! { #b });
}
output.push(parse_quote! { 0 });
}
struct UnsupportedField;
fn parse_event_field(
errors: &mut Vec<Error>,
well_known_types: &WellKnownTypes,
event_attr: Option<&syn::Attribute>,
field_span: proc_macro2::Span,
field_name: &Ident,
field_ty: &mut syn::Type,
data_descriptor_array: &mut TokenStream,
event_metadata: &mut Vec<syn::Expr>,
statements: &mut TokenStream,
) -> Result<(), UnsupportedField> {
let param_name_string = field_name.to_string();
append_utf8_str_chars(event_metadata, ¶m_name_string);
let mut output_hex = false;
if let Some(event_attr) = event_attr {
match event_attr.parse_meta() {
Ok(syn::Meta::List(list)) => {
for item in list.nested.iter() {
match item {
syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
path,
lit,
..
})) => {
if *path == parse_quote!(output) {
match &lit {
Lit::Str(lit) => {
let output_string = lit.value();
match output_string.as_str() {
"hex" => {
output_hex = true;
}
_ => {
errors.push(Error::new_spanned(
path,
"Output format is not recognized.",
));
}
}
}
_ => errors.push(Error::new_spanned(
path,
"This metadata is expected to be a string.",
)),
}
} else {
errors.push(Error::new_spanned(
path,
"This metadata key is not recognized.",
));
}
}
_ => errors.push(Error::new_spanned(
item,
"This metadata item is not recognized.",
)),
}
}
}
Ok(_) => errors.push(Error::new_spanned(
event_attr,
"This metadata is not recognized.",
)),
Err(e) => {
errors.push(e);
}
}
}
let mut field_metadata_intype: Expr;
let mut field_metadata_out_type: Option<Expr> = None;
if let Some(t) = well_known_types.find(&*field_ty) {
field_metadata_intype = if let Some(in_type_expr) = t.opts.in_type_expr.as_ref() {
in_type_expr.clone()
} else {
let in_type: u8 = t.in_type.bits();
parse_quote!(#in_type)
};
if let Some(out_type) = t.opts.out_type {
let out_type: u8 = out_type.bits();
field_metadata_out_type = Some(parse_quote!(#out_type));
} else {
field_metadata_out_type = None;
}
if let Some(r) = t.opts.replacement_type.as_ref() {
*field_ty = r.clone();
}
match t.code {
WellKnownType::ref_str => {
let field_len = ident_suffix(field_name, "len");
statements.extend(quote_spanned! {
field_span =>
let #field_len: u16 = #field_name.len() as u16;
});
data_descriptor_array.extend(quote! {
EventDataDescriptor::from(&#field_len),
EventDataDescriptor::from(#field_name),
});
}
WellKnownType::u16cstr => {
let field_len = ident_suffix(field_name, "len");
statements.extend(quote! {
let #field_len: usize = #field_name.len();
let #field_len: u16 = #field_len.min(0xffff) as u16;
});
data_descriptor_array.extend(quote! {
EventDataDescriptor::from(&#field_len),
EventDataDescriptor::from(#field_name),
});
}
WellKnownType::osstr => {
let field_len = ident_suffix(field_name, "len");
let field_desc = ident_suffix(field_name, "desc");
let field_u16cstring = ident_suffix(field_name, "u16cstring");
let field_u16cstr = ident_suffix(field_name, "u16cstr");
statements.extend(quote! {
let #field_desc: EventDataDescriptor;
let #field_len: u16;
let #field_u16cstring: ::widestring::U16CString;
let #field_u16cstr: &::widestring::U16CStr;
match ::widestring::U16CString::from_os_str(#field_name) {
Ok(s) => {
#field_u16cstring = s;
#field_u16cstr = #field_u16cstring.as_ref();
#field_desc = EventDataDescriptor::from(#field_u16cstr);
#field_len = #field_u16cstr.len() as u16;
}
Err(_) => {
#field_desc = EventDataDescriptor::empty();
#field_len = 0;
}
}
});
data_descriptor_array.extend(quote! {
EventDataDescriptor::from(&#field_len),
#field_desc,
});
}
WellKnownType::SocketAddrV4 => {
let field_len = ident_suffix(field_name, "len");
statements.extend(quote_spanned! {
field_span =>
let #field_name = ::win_etw_provider::SocketAddrV4::from(#field_name);
let #field_len: u16 = (::core::mem::size_of::<::win_etw_provider::SocketAddrV4>()) as u16;
});
data_descriptor_array.extend(quote! {
EventDataDescriptor::from(&#field_len),
EventDataDescriptor::from(&#field_name),
});
}
WellKnownType::SocketAddrV6 => {
let field_len = ident_suffix(field_name, "len");
statements.extend(quote_spanned! {
field_span =>
let #field_name = ::win_etw_provider::SocketAddrV6::from(#field_name);
let #field_len: u16 = (::core::mem::size_of::<::win_etw_provider::SocketAddrV6>()) as u16;
});
data_descriptor_array.extend(quote_spanned! {
field_span =>
EventDataDescriptor::from(&#field_len),
EventDataDescriptor::from(&#field_name),
});
}
WellKnownType::SocketAddr => {
let field_desc = ident_suffix(field_name, "desc");
let field_v4 = ident_suffix(field_name, "v4");
let field_v6 = ident_suffix(field_name, "v6");
let field_len_ident = ident_suffix(field_name, "len");
statements.extend(quote_spanned! {
field_span =>
let #field_v4;
let #field_v6;
let #field_len_ident;
let #field_desc;
match #field_name {
::std::net::SocketAddr::V4(ref address_v4) => {
#field_v4 = ::win_etw_provider::SocketAddrV4::from(address_v4);
#field_len_ident = ::core::mem::size_of::<::win_etw_provider::SocketAddrV4>() as u16;
#field_desc = EventDataDescriptor::from(&#field_v4);
}
::std::net::SocketAddr::V6(ref address_v6) => {
#field_v6 = ::win_etw_provider::SocketAddrV6::from(address_v6);
#field_len_ident = ::core::mem::size_of::<::win_etw_provider::SocketAddrV6>() as u16;
#field_desc = EventDataDescriptor::from(&#field_v6);
}
}
});
data_descriptor_array.extend(quote_spanned! {
field_span =>
EventDataDescriptor::from(&#field_len_ident),
#field_desc,
});
}
WellKnownType::SystemTime => {
statements.extend(quote_spanned!{
field_span =>
let #field_name = <::win_etw_provider::FILETIME as ::core::convert::TryFrom<::std::time::SystemTime>>
::try_from(#field_name);
});
data_descriptor_array.extend(quote_spanned! {
field_span =>
match &#field_name {
Ok(ref t) => EventDataDescriptor::from(&t.0),
Err(_) => EventDataDescriptor::empty(),
}
});
}
WellKnownType::FILETIME => {
data_descriptor_array.extend(quote_spanned! {
field_span =>
EventDataDescriptor::from(&#field_name.0),
});
}
WellKnownType::bool => {
statements.extend(quote_spanned! {
field_span =>
let #field_name: i8 = #field_name as i8;
});
data_descriptor_array.extend(quote! {
EventDataDescriptor::from(&#field_name),
});
}
_ => {
if t.is_ref {
data_descriptor_array.extend(quote_spanned! {
field_span =>
EventDataDescriptor::from(#field_name),
});
} else {
data_descriptor_array.extend(quote_spanned! {
field_span =>
EventDataDescriptor::from(&#field_name),
});
}
}
}
} else {
match &*field_ty {
syn::Type::Reference(ref_ty) => {
match &*ref_ty.elem {
syn::Type::Slice(slice_ty) => {
if let Some(t) = well_known_types.find(&slice_ty.elem) {
if !t.primitive {
return Err(UnsupportedField);
}
let field_len_ident = ident_suffix(field_name, "len");
statements.extend(quote_spanned! {
field_span =>
let #field_name = &#field_name[..#field_name.len().min(0xffff)];
let #field_len_ident: u16 = #field_name.len() as u16;
});
data_descriptor_array.extend(quote! {
EventDataDescriptor::from(&#field_len_ident),
EventDataDescriptor::from(#field_name),
});
let in_type_u8 = t.in_type.bits();
field_metadata_intype = parse_quote!(#in_type_u8);
field_metadata_intype = parse_quote!(#field_metadata_intype | ::win_etw_provider::metadata::InFlag::VCOUNT_FLAG.bits());
} else {
return Err(UnsupportedField);
}
}
_ => {
return Err(UnsupportedField);
}
}
}
_ => {
return Err(UnsupportedField);
}
}
}
if output_hex {
let hex: Expr = parse_quote!(::win_etw_provider::metadata::OutFlag::HEX.bits());
field_metadata_out_type = Some(if let Some(out_type) = field_metadata_out_type {
parse_quote!(#out_type | #hex)
} else {
hex
});
}
if let Some(out_type) = field_metadata_out_type {
field_metadata_intype = parse_quote!(#field_metadata_intype | ::win_etw_provider::metadata::InFlag::CHAIN_FLAG.bits());
event_metadata.push(field_metadata_intype);
event_metadata.push(out_type);
} else {
event_metadata.push(field_metadata_intype);
}
Ok(())
}
use errors::CombinedErrors;
#[derive(Default, Debug)]
struct ProviderAttributes {
uuid: Uuid,
provider_name: Option<String>,
}
impl syn::parse::Parse for ProviderAttributes {
fn parse(stream: syn::parse::ParseStream) -> syn::Result<Self> {
let mut errors = CombinedErrors::default();
let mut uuid_opt = None;
let items: syn::punctuated::Punctuated<syn::Meta, Token![,]> =
stream.parse_terminated(syn::Meta::parse)?;
let mut provider_name = None;
for item in items.iter() {
errors.scope(item.span(), |scope| {
match item {
syn::Meta::NameValue(syn::MetaNameValue { path, lit, .. }) => {
if *path == parse_quote!(guid) {
let s = if let syn::Lit::Str(s) = lit {
s
} else {
scope.msg(
"The attribute value is required to be a GUID in string form.",
);
return Ok(());
};
let guid_str = s.value();
let uuid = if let Ok(value) = guid_str.parse::<Uuid>() {
if value == Uuid::nil() {
scope.msg("The GUID cannot be the NIL (all-zeroes) GUID.");
}
value
} else {
scope.msg("The attribute value is required to be a valid GUID.");
Uuid::nil()
};
if uuid_opt.is_some() {
scope.msg(
"The 'guid' attribute key cannot be specified more than once.",
);
} else {
uuid_opt = Some(uuid);
}
} else if *path == parse_quote!(name) {
if let syn::Lit::Str(s) = lit {
if provider_name.is_none() {
provider_name = Some(s.value());
} else {
scope.msg("The 'name' attribute can only be specified once.");
}
} else {
scope.msg("The 'name' attribute key requires a string value.");
}
} else {
scope.msg("Unrecognized attribute key.");
}
}
syn::Meta::Path(path) if *path == parse_quote!(static_mode) => {
}
_ => {
scope.msg("Unrecognized attribute item.");
}
}
Ok(())
});
}
let uuid = if let Some(provider_uuid) = uuid_opt {
provider_uuid
} else {
errors.push(Error::new_spanned(
&items,
"The 'guid' attribute is required.
Please generate a GUID that uniquely identfies this event provider.
Do not use the same GUID for different event providers.
Example: #[trace_logging_provider(guid = \"123e4567-e89b...\")]",
));
Uuid::nil()
};
errors.into_result(ProviderAttributes {
uuid,
provider_name,
})
}
}
fn uuid_to_expr(uuid: &Uuid) -> syn::Expr {
let bytes: &[u8; 16] = uuid.as_bytes();
let data1: u32 = ((bytes[0] as u32) << 24)
| ((bytes[1] as u32) << 16)
| ((bytes[2] as u32) << 8)
| (bytes[3] as u32);
let data2: u16 = ((bytes[4] as u16) << 8) | (bytes[5] as u16);
let data3: u16 = ((bytes[6] as u16) << 8) | (bytes[7] as u16);
let data4_0 = bytes[8];
let data4_1 = bytes[9];
let data4_2 = bytes[10];
let data4_3 = bytes[11];
let data4_4 = bytes[12];
let data4_5 = bytes[13];
let data4_6 = bytes[14];
let data4_7 = bytes[15];
return parse_quote! {
::win_etw_provider::GUID {
data1: #data1,
data2: #data2,
data3: #data3,
data4: [
#data4_0,
#data4_1,
#data4_2,
#data4_3,
#data4_4,
#data4_5,
#data4_6,
#data4_7,
]
}
};
}
struct EventAttributes {
level: syn::Expr,
opcode: syn::Expr,
task: syn::Expr,
method_attrs: Vec<syn::Attribute>,
}
fn parse_event_attributes(
errors: &mut Vec<Error>,
method_ident: &Ident,
input_method_attrs: &[syn::Attribute],
) -> EventAttributes {
let mut level: Expr = parse_quote!(::win_etw_provider::Level::INFO);
let mut opcode: Expr = parse_quote!(0);
let mut task: Expr = parse_quote!(0);
let mut method_attrs: Vec<syn::Attribute> = Vec::new();
let mut event_already_has_doc = false;
let enable_default_event_doc = false;
for attr in input_method_attrs.iter() {
if attr.path == parse_quote!(doc) {
method_attrs.push(attr.clone());
event_already_has_doc = true;
} else if attr.path == parse_quote!(event) {
match attr.parse_meta() {
Ok(syn::Meta::List(list)) => {
for item in list.nested.iter() {
match item {
syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
path,
lit,
..
})) => {
if *path == parse_quote!(level) {
match lit {
Lit::Str(lit_str) => {
let level_ident = match lit_str.value().as_str() {
"critical" => quote!(CRITICAL),
"error" => quote!(ERROR),
"warn" => quote!(WARN),
"info" => quote!(INFO),
"verbose" => quote!(VERBOSE),
_ => {
errors.push(Error::new_spanned(item, "The value specified for 'level' is not a valid string."));
quote!(INFO)
}
};
level = parse_quote!(::win_etw_provider::Level::#level_ident);
}
Lit::Int(_) => {
level = parse_quote!(::win_etw_provider::Level(#lit));
}
_ => {
errors.push(Error::new_spanned(item, "The value specified for 'level' is not recognized."));
}
}
} else if *path == parse_quote!(opcode) {
opcode = Expr::Lit(ExprLit {
lit: lit.clone(),
attrs: Vec::new(),
});
} else if *path == parse_quote!(task) {
task = Expr::Lit(ExprLit {
lit: lit.clone(),
attrs: Vec::new(),
});
} else {
errors
.push(Error::new_spanned(item, "Unrecognized attribute."));
}
}
_ => {
errors.push(Error::new_spanned(item, "Unrecognized attribute."));
}
}
}
}
Ok(_) => {
errors.push(Error::new_spanned(
attr,
"The form of the #[event] attribute is invalid. \
It should be: #[event(name = \"value\", name2 = \"value2\", ...)].",
));
}
Err(e) => {
errors.push(e);
}
}
} else {
errors.push(Error::new_spanned(
attr,
"The only attributes allowed on event methods are #[doc] and #[event(...)] attributes.",
));
}
}
if !event_already_has_doc && enable_default_event_doc {
let method_doc = format!(
"Writes the `{}` event to the ETW log stream.",
method_ident.to_string()
);
method_attrs.push(parse_quote!( #![doc = #method_doc] ));
}
EventAttributes {
method_attrs,
level,
opcode,
task,
}
}
const IDENT_SEPARATOR: &str = "__";
fn ident_suffix(ident: &Ident, suffix: &str) -> Ident {
Ident::new(
&format!("{}{}{}", ident.to_string(), IDENT_SEPARATOR, suffix),
ident.span(),
)
}