#[cfg(feature = "std")]
use std::sync::OnceLock;
#[cfg(feature = "trace")]
use super::trace::ReportTrace;
#[cfg(feature = "std")]
use super::types::GlobalContext;
use super::{MissingSeverity, Report, ReportMetadata, ReportOptions, SeverityState};
use alloc::boxed::Box;
#[cfg(feature = "std")]
pub(crate) type ContextInjector = dyn Fn() -> Option<GlobalContext> + Send + Sync + 'static;
#[cfg(feature = "std")]
pub(crate) fn global_context_injector() -> &'static OnceLock<Box<ContextInjector>> {
static INJECTOR: OnceLock<Box<ContextInjector>> = OnceLock::new();
&INJECTOR
}
#[cfg(feature = "std")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RegisterGlobalContextError;
#[cfg(feature = "std")]
pub fn register_global_injector(
injector: impl Fn() -> Option<GlobalContext> + Send + Sync + 'static,
) -> Result<(), RegisterGlobalContextError> {
global_context_injector()
.set(Box::new(injector))
.map_err(|_| RegisterGlobalContextError)
}
impl<E, State> Report<E, State>
where
State: SeverityState,
{
#[cfg(feature = "std")]
fn apply_global_context(mut self) -> Self {
let Some(injector) = global_context_injector().get() else {
return self;
};
let injected = std::panic::catch_unwind(std::panic::AssertUnwindSafe(injector));
let Some(global) = injected.unwrap_or_default() else {
return self;
};
let GlobalContext {
#[cfg(feature = "trace")]
trace,
error,
system,
context,
} = &global;
let needs_allocation = {
let has_error_meta = error
.as_ref()
.map(|e| e.error_code.is_some() || e.category.is_some() || e.retryable.is_some())
.unwrap_or(false);
let has_system = !system.is_empty();
let has_context = !context.is_empty();
#[cfg(feature = "trace")]
let has_trace = trace.is_some();
#[cfg(not(feature = "trace"))]
let has_trace = false;
has_error_meta || has_system || has_context || has_trace
};
if !needs_allocation {
return self;
}
if let Some(error) = global.error {
if let Some(error_code) = error.error_code {
self.data.metadata.with_error_code_mut(error_code);
}
if let Some(category) = error.category {
self.data.metadata.with_category_mut(category);
}
if let Some(retryable) = error.retryable {
self.data.metadata.with_retryable_mut(retryable);
}
}
if !global.system.is_empty() {
*self.data.bag.system_mut() = global.system;
}
if !global.context.is_empty() {
for (key, value) in &global.context {
self.data.bag.insert_context(key.clone(), value.clone());
}
}
#[cfg(feature = "trace")]
if let Some(global_trace) = global.trace {
let trace = core::mem::take(&mut self.data.trace);
self.data.trace = trace
.set_trace_id_opt(global_trace.trace_id)
.set_span_id_opt(global_trace.span_id)
.set_parent_span_id_opt(global_trace.parent_span_id)
.set_sampled_opt(global_trace.sampled)
.set_trace_state_opt(global_trace.trace_state);
}
self
}
}
impl<E> Report<E, crate::report::MissingSeverity> {
pub fn new(inner: E) -> Self {
let report = Self {
inner,
data: Box::new(super::ReportData {
metadata: ReportMetadata::new(),
options: ReportOptions::new(),
#[cfg(feature = "trace")]
trace: ReportTrace::default(),
bag: super::DiagnosticBag::new(),
}),
};
#[cfg(feature = "std")]
return report.apply_global_context();
#[cfg(not(feature = "std"))]
return report;
}
pub fn with_severity(self, severity: super::Severity) -> Report<E, super::HasSeverity> {
let Self { inner, data } = self;
let super::ReportData {
metadata,
options,
#[cfg(feature = "trace")]
trace,
bag,
} = *data;
Report {
inner,
data: Box::new(super::ReportData {
metadata: ReportMetadata::<MissingSeverity>::set_severity(metadata, severity),
options,
#[cfg(feature = "trace")]
trace,
bag,
}),
}
}
}