use core::error::Error;
use core::fmt;
#[cfg(not(target_has_atomic = "ptr"))]
use core::cell::Cell;
#[cfg(target_has_atomic = "ptr")]
use core::sync::atomic::{AtomicUsize, Ordering};
#[cfg(feature = "alloc")]
use crate::alloc::Global;
use crate::{Allocator, Context};
use super::{
Capture, ContextError, Emit, ErrorMode, Errors, Ignore, NoTrace, Report, Trace, TraceImpl,
TraceMode,
};
pub struct DefaultContext<A, T, C>
where
A: Allocator,
T: TraceMode,
{
alloc: A,
trace: T::Impl<A>,
capture: C,
depth: Depth,
nesting_limit: Option<usize>,
}
pub const DEFAULT_NESTING_LIMIT: usize = 128;
struct Depth {
#[cfg(target_has_atomic = "ptr")]
value: AtomicUsize,
#[cfg(not(target_has_atomic = "ptr"))]
value: Cell<usize>,
}
impl Depth {
#[inline]
const fn new() -> Self {
Self {
#[cfg(target_has_atomic = "ptr")]
value: AtomicUsize::new(0),
#[cfg(not(target_has_atomic = "ptr"))]
value: Cell::new(0),
}
}
#[inline]
fn get(&self) -> usize {
#[cfg(target_has_atomic = "ptr")]
{
self.value.load(Ordering::Relaxed)
}
#[cfg(not(target_has_atomic = "ptr"))]
{
self.value.get()
}
}
#[inline]
fn set(&self, value: usize) {
#[cfg(target_has_atomic = "ptr")]
{
self.value.store(value, Ordering::Relaxed);
}
#[cfg(not(target_has_atomic = "ptr"))]
{
self.value.set(value);
}
}
}
#[cfg(feature = "alloc")]
impl DefaultContext<Global, NoTrace, Ignore> {
#[inline]
pub(super) fn new() -> Self {
Self::new_in(Global::new())
}
}
impl<A> DefaultContext<A, NoTrace, Ignore>
where
A: Allocator,
{
#[inline]
pub(super) fn new_in(alloc: A) -> Self {
let trace = NoTrace::new_in(alloc);
Self {
alloc,
trace,
capture: Ignore,
depth: Depth::new(),
nesting_limit: Some(DEFAULT_NESTING_LIMIT),
}
}
}
#[cfg(feature = "alloc")]
impl Default for DefaultContext<Global, NoTrace, Ignore> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<A, T, C> DefaultContext<A, T, C>
where
A: Allocator,
T: TraceMode,
C: ErrorMode<A>,
{
#[inline]
pub fn with_trace(self) -> DefaultContext<A, Trace, C> {
let trace = Trace::new_in(self.alloc);
DefaultContext {
alloc: self.alloc,
trace,
capture: self.capture,
depth: Depth::new(),
nesting_limit: self.nesting_limit,
}
}
#[inline]
pub fn with_capture<E>(self) -> DefaultContext<A, T, Capture<E>>
where
E: ContextError<A>,
{
DefaultContext {
alloc: self.alloc,
trace: self.trace,
capture: Capture::new(),
depth: Depth::new(),
nesting_limit: self.nesting_limit,
}
}
#[inline]
pub fn with_error<E>(self) -> DefaultContext<A, T, Emit<E>>
where
E: ContextError<A>,
{
DefaultContext {
alloc: self.alloc,
trace: self.trace,
capture: Emit::new(),
depth: Depth::new(),
nesting_limit: self.nesting_limit,
}
}
#[inline]
pub fn with_nesting_limit(mut self, limit: usize) -> Self {
self.nesting_limit = Some(limit);
self
}
#[inline]
pub fn without_nesting_limit(mut self) -> Self {
self.nesting_limit = None;
self
}
}
impl<A, C> DefaultContext<A, Trace, C>
where
A: Allocator,
{
#[inline]
pub fn with_type(mut self) -> Self {
self.trace.include_type();
self
}
#[inline]
pub fn report(&self) -> Report<'_, A> {
self.trace.report()
}
#[inline]
pub fn errors(&self) -> Errors<'_, A> {
self.trace.errors()
}
}
impl<A, T, E> DefaultContext<A, T, Capture<E>>
where
A: Allocator,
T: TraceMode,
E: ContextError<A>,
{
#[inline]
pub fn unwrap(&self) -> E {
self.capture.unwrap()
}
#[inline]
pub fn result(&self) -> Result<(), E> {
self.capture.result()
}
}
impl<A, T, C> Context for &DefaultContext<A, T, C>
where
A: Allocator,
T: TraceMode,
C: ErrorMode<A>,
{
type Error = C::Error;
type Mark = <<T as TraceMode>::Impl<A> as TraceImpl<A>>::Mark;
type Allocator = A;
#[inline]
fn clear(self) {
self.trace.clear();
self.capture.clear();
self.depth.set(0);
}
#[inline]
fn alloc(self) -> Self::Allocator {
self.alloc
}
#[inline]
fn custom<E>(self, message: E) -> Self::Error
where
E: 'static + Send + Sync + Error,
{
self.trace.custom(self.alloc, &message);
match self.trace.location(None) {
Some(location) => self.capture.traced_custom(self.alloc, message, &location),
None => self.capture.custom(self.alloc, message),
}
}
#[inline]
fn message<M>(self, message: M) -> Self::Error
where
M: fmt::Display,
{
self.trace.message(self.alloc, &message);
match self.trace.location(None) {
Some(location) => self.capture.traced_message(self.alloc, message, &location),
None => self.capture.message(self.alloc, message),
}
}
#[inline]
fn message_at<M>(self, mark: &Self::Mark, message: M) -> Self::Error
where
M: fmt::Display,
{
self.trace.message_at(self.alloc, mark, &message);
match self.trace.location(Some(mark)) {
Some(location) => self.capture.traced_message(self.alloc, message, &location),
None => self.capture.message(self.alloc, message),
}
}
#[inline]
fn custom_at<E>(self, mark: &Self::Mark, message: E) -> Self::Error
where
E: 'static + Send + Sync + Error,
{
self.trace.custom_at(self.alloc, mark, &message);
match self.trace.location(Some(mark)) {
Some(location) => self.capture.traced_custom(self.alloc, message, &location),
None => self.capture.custom(self.alloc, message),
}
}
#[inline]
fn mark(self) -> Self::Mark {
self.trace.mark()
}
#[inline]
fn restore(self, mark: &Self::Mark) {
self.trace.restore(mark);
}
#[inline]
fn advance(self, n: usize) {
self.trace.advance(n);
}
#[inline]
fn enter_nesting(self) -> Result<(), Self::Error> {
let Some(limit) = self.nesting_limit else {
return Ok(());
};
let depth = self.depth.get();
if depth >= limit {
return Err(self.message(NestingLimitExceeded { limit }));
}
self.depth.set(depth + 1);
Ok(())
}
#[inline]
fn leave_nesting(self) {
if self.nesting_limit.is_some() {
self.depth.set(self.depth.get().saturating_sub(1));
}
}
#[inline]
fn enter_named_field<F>(self, name: &'static str, field: F)
where
F: fmt::Display,
{
self.trace.enter_named_field(name, &field);
}
#[inline]
fn enter_unnamed_field<F>(self, index: u32, name: F)
where
F: fmt::Display,
{
self.trace.enter_unnamed_field(index, &name);
}
#[inline]
fn leave_field(self) {
self.trace.leave_field();
}
#[inline]
fn enter_struct(self, name: &'static str) {
self.trace.enter_struct(name);
}
#[inline]
fn leave_struct(self) {
self.trace.leave_struct();
}
#[inline]
fn enter_enum(self, name: &'static str) {
self.trace.enter_enum(name);
}
#[inline]
fn leave_enum(self) {
self.trace.leave_enum();
}
#[inline]
fn enter_variant<V>(self, name: &'static str, tag: V)
where
V: fmt::Display,
{
self.trace.enter_variant(name, &tag);
}
#[inline]
fn leave_variant(self) {
self.trace.leave_variant();
}
#[inline]
fn enter_sequence_index(self, index: usize) {
self.trace.enter_sequence_index(index);
}
#[inline]
fn leave_sequence_index(self) {
self.trace.leave_sequence_index();
}
#[inline]
fn enter_map_key<F>(self, field: F)
where
F: fmt::Display,
{
self.trace.enter_map_key(self.alloc, &field);
}
#[inline]
fn leave_map_key(self) {
self.trace.leave_map_key();
}
}
struct NestingLimitExceeded {
limit: usize,
}
impl fmt::Display for NestingLimitExceeded {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Recursion limit exceeded, input may be nested at most {} levels deep",
self.limit
)
}
}