mirror of
https://github.com/tokio-rs/tracing.git
synced 2025-09-28 05:21:57 +00:00
macros: Remove 'r#' prefix from raw identifiers in field names (#3130)
* macros: Add test involving raw identifier * macros: Remove 'r#' prefix from raw identifiers in field names
This commit is contained in:
parent
cffdc830d6
commit
6f1ac9d791
@ -987,10 +987,11 @@ pub mod subscriber;
|
||||
pub mod __macro_support {
|
||||
pub use crate::callsite::Callsite;
|
||||
use crate::{subscriber::Interest, Metadata};
|
||||
use core::{fmt, str};
|
||||
// Re-export the `core` functions that are used in macros. This allows
|
||||
// a crate to be named `core` and avoid name clashes.
|
||||
// See here: https://github.com/tokio-rs/tracing/issues/2761
|
||||
pub use core::{concat, file, format_args, iter::Iterator, line, option::Option};
|
||||
pub use core::{concat, file, format_args, iter::Iterator, line, option::Option, stringify};
|
||||
|
||||
/// Callsite implementation used by macro-generated code.
|
||||
///
|
||||
@ -1065,6 +1066,66 @@ pub mod __macro_support {
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Implementation detail used for constructing FieldSet names from raw
|
||||
/// identifiers. In `info!(..., r#type = "...")` the macro would end up
|
||||
/// constructing a name equivalent to `FieldName(*b"type")`.
|
||||
pub struct FieldName<const N: usize>([u8; N]);
|
||||
|
||||
impl<const N: usize> FieldName<N> {
|
||||
/// Convert `"prefix.r#keyword.suffix"` to `b"prefix.keyword.suffix"`.
|
||||
pub const fn new(input: &str) -> Self {
|
||||
let input = input.as_bytes();
|
||||
let mut output = [0u8; N];
|
||||
let mut read = 0;
|
||||
let mut write = 0;
|
||||
while read < input.len() {
|
||||
if read + 1 < input.len() && input[read] == b'r' && input[read + 1] == b'#' {
|
||||
read += 2;
|
||||
}
|
||||
output[write] = input[read];
|
||||
read += 1;
|
||||
write += 1;
|
||||
}
|
||||
assert!(write == N);
|
||||
Self(output)
|
||||
}
|
||||
|
||||
pub const fn as_str(&self) -> &str {
|
||||
// SAFETY: Because of the private visibility of self.0, it must have
|
||||
// been computed by Self::new. So these bytes are all of the bytes
|
||||
// of some original valid UTF-8 string, but with "r#" substrings
|
||||
// removed, which cannot have produced invalid UTF-8.
|
||||
unsafe { str::from_utf8_unchecked(self.0.as_slice()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldName<0> {
|
||||
/// For `"prefix.r#keyword.suffix"` compute `"prefix.keyword.suffix".len()`.
|
||||
pub const fn len(input: &str) -> usize {
|
||||
// Count occurrences of "r#"
|
||||
let mut raw = 0;
|
||||
|
||||
let mut i = 0;
|
||||
while i < input.len() {
|
||||
if input.as_bytes()[i] == b'#' {
|
||||
raw += 1;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
input.len() - 2 * raw
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> fmt::Debug for FieldName<N> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter
|
||||
.debug_tuple("FieldName")
|
||||
.field(&self.as_str())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "log")]
|
||||
|
@ -3096,9 +3096,12 @@ macro_rules! level_to_log {
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __tracing_stringify {
|
||||
($($t:tt)*) => {
|
||||
stringify!($($t)*)
|
||||
};
|
||||
($($k:ident).+) => {{
|
||||
const NAME: $crate::__macro_support::FieldName<{
|
||||
$crate::__macro_support::FieldName::len($crate::__macro_support::stringify!($($k).+))
|
||||
}> = $crate::__macro_support::FieldName::new($crate::__macro_support::stringify!($($k).+));
|
||||
NAME.as_str()
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "log"))]
|
||||
|
@ -618,3 +618,15 @@ fn keyword_ident_in_field_name() {
|
||||
with_default(subscriber, || error!(crate = "tracing", "message"));
|
||||
handle.assert_finished();
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
|
||||
#[test]
|
||||
fn raw_ident_in_field_name() {
|
||||
let (subscriber, handle) = subscriber::mock()
|
||||
.event(expect::event().with_fields(expect::field("this.type").with_value(&"Value")))
|
||||
.only()
|
||||
.run_with_handle();
|
||||
|
||||
with_default(subscriber, || error!(this.r#type = "Value"));
|
||||
handle.assert_finished();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user