use std::collections::HashMap;
use std::fmt;
use tracing_core::field::{Field, Visit};
pub(crate) struct FieldMapping {
pub field_name: &'static str,
pub context_key: &'static str,
pub setter: Box<dyn FieldSetter>,
}
pub(crate) trait FieldSetter: Send + Sync {
fn set_from_str(&self, key: &'static str, value: &str);
fn set_from_u64(&self, key: &'static str, value: u64);
fn set_from_i64(&self, key: &'static str, value: i64);
fn set_from_bool(&self, key: &'static str, value: bool);
}
pub trait FromFieldValue: Clone + Default + Send + Sync + serde::Serialize + serde::de::DeserializeOwned + 'static {
fn from_str_value(s: &str) -> Option<Self> {
let _ = s;
None
}
fn from_u64_value(v: u64) -> Option<Self> {
let _ = v;
None
}
fn from_i64_value(v: i64) -> Option<Self> {
let _ = v;
None
}
fn from_bool_value(v: bool) -> Option<Self> {
let _ = v;
None
}
}
pub(crate) struct TypedFieldSetter<T> {
_marker: std::marker::PhantomData<T>,
}
impl<T> TypedFieldSetter<T> {
pub fn new() -> Self {
Self {
_marker: std::marker::PhantomData,
}
}
}
impl<T> FieldSetter for TypedFieldSetter<T>
where
T: FromFieldValue,
{
fn set_from_str(&self, key: &'static str, value: &str) {
if let Some(v) = T::from_str_value(value) {
dcontext::set_context(key, v);
}
}
fn set_from_u64(&self, key: &'static str, value: u64) {
if let Some(v) = T::from_u64_value(value) {
dcontext::set_context(key, v);
}
}
fn set_from_i64(&self, key: &'static str, value: i64) {
if let Some(v) = T::from_i64_value(value) {
dcontext::set_context(key, v);
}
}
fn set_from_bool(&self, key: &'static str, value: bool) {
if let Some(v) = T::from_bool_value(value) {
dcontext::set_context(key, v);
}
}
}
pub(crate) struct ExtractedFields {
pub string_values: HashMap<&'static str, String>,
pub u64_values: HashMap<&'static str, u64>,
pub i64_values: HashMap<&'static str, i64>,
pub bool_values: HashMap<&'static str, bool>,
}
impl ExtractedFields {
pub fn new() -> Self {
Self {
string_values: HashMap::new(),
u64_values: HashMap::new(),
i64_values: HashMap::new(),
bool_values: HashMap::new(),
}
}
pub fn is_empty(&self) -> bool {
self.string_values.is_empty()
&& self.u64_values.is_empty()
&& self.i64_values.is_empty()
&& self.bool_values.is_empty()
}
}
pub(crate) struct FieldExtractor<'a> {
pub target_fields: &'a [&'static str],
pub extracted: ExtractedFields,
}
impl<'a> FieldExtractor<'a> {
pub fn new(target_fields: &'a [&'static str]) -> Self {
Self {
target_fields,
extracted: ExtractedFields::new(),
}
}
fn is_target(&self, field: &Field) -> Option<&'static str> {
self.target_fields.iter().find(|&&f| f == field.name()).copied()
}
}
impl<'a> Visit for FieldExtractor<'a> {
fn record_str(&mut self, field: &Field, value: &str) {
if let Some(key) = self.is_target(field) {
self.extracted.string_values.insert(key, value.to_string());
}
}
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
if let Some(key) = self.is_target(field) {
if !self.extracted.string_values.contains_key(key)
&& !self.extracted.u64_values.contains_key(key)
&& !self.extracted.i64_values.contains_key(key)
&& !self.extracted.bool_values.contains_key(key)
{
self.extracted
.string_values
.insert(key, format!("{:?}", value));
}
}
}
fn record_u64(&mut self, field: &Field, value: u64) {
if let Some(key) = self.is_target(field) {
self.extracted.u64_values.insert(key, value);
}
}
fn record_i64(&mut self, field: &Field, value: i64) {
if let Some(key) = self.is_target(field) {
self.extracted.i64_values.insert(key, value);
}
}
fn record_bool(&mut self, field: &Field, value: bool) {
if let Some(key) = self.is_target(field) {
self.extracted.bool_values.insert(key, value);
}
}
}