yoshi_std/
lib.rs

1/* yoshi/yoshi-std/src/lib.rs */
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![allow(dead_code)]
4#![warn(clippy::all)]
5#![warn(missing_docs)]
6#![warn(clippy::cargo)]
7#![warn(clippy::pedantic)]
8#![allow(clippy::too_many_lines)] // Allows for longer files, often necessary in comprehensive modules
9#![allow(clippy::result_large_err)] // Error handling framework intentionally uses large error types for rich context
10#![allow(clippy::enum_variant_names)] // For consistent naming of enum variants like IoError.
11#![allow(clippy::items_after_statements)] // Allows declaration of items after statements for better code flow in some contexts
12#![allow(clippy::module_name_repetitions)] // Allow for names like YoshiKind, YoContext.
13#![cfg_attr(not(feature = "std"), no_std)]
14//! **Brief:** Comprehensive error handling framework for robust Rust applications.
15//!
16//! Yoshi provides structured error types with rich contextual information, making it easier
17//! to debug, trace, and handle errors throughout your application. It offers flexible error
18//! categorization, context chaining, and optional backtrace capture while maintaining
19//! excellent performance characteristics.
20//!
21//! **Module Classification:** Performance-Critical\
22//! **Complexity Level:** Expert\
23//! **API Stability:** Stable
24//!
25//! ## Key Features
26//!
27//! **Structured Error Types**: Define precise error categories with relevant metadata
28//! rather than relying on string-based errors. Each error kind captures the specific
29//! information needed for that failure mode.
30//!
31//! **Rich Context**: Add diagnostic information, suggestions, and typed payloads
32//! as errors propagate through your application. Context is preserved without
33//! performance overhead.
34//!
35//! **Performance Focused**: Sub-microsecond error creation with O(1) context
36//! attachment. Backtrace capture is conditional and can be disabled in production.
37//!
38//! **`no_std` Compatible**: Full functionality available in `no_std` environments
39//! with automatic fallbacks for platform-specific features.
40//!
41//! ## Usage Patterns
42//!
43//! Yoshi works well for applications that need detailed error diagnostics and
44//! structured error handling. It's particularly useful when you want to:
45//!
46//! - Provide rich debugging information to developers
47//! - Maintain error context across call stacks
48//! - Categorize errors for different handling strategies
49//! - Include suggestions and metadata for error recovery
50//!
51//! For simpler error propagation needs, consider [`anyhow`]. For derive-based
52//! error definitions, [`thiserror`] remains an excellent choice and can be
53//! used alongside Yoshi.
54//!
55//! ## Core Types
56//!
57//! - [`Yoshi`]: The main error type providing structured error handling
58//! - [`YoshiKind`]: Error categories with type-specific fields
59//! - [`YoContext`]: Contextual information and metadata
60//! - [`HatchExt`]: Extension trait for `Result` types
61//! - [`YoshiLocation`]: Source code location capture
62//! - [`YoshiBacktrace`]: Performance-monitored backtrace wrapper
63//! - `NoStdIo`: I/O error type for `no_std` environments
64//! - [`Result`]: Type alias for `Result` with `Yoshi` as default error
65//! - [`error_instance_count()`]: Global counter for Yoshi error instances
66//!
67//! # Examples
68//!
69//! Basic error creation and context addition:
70//!
71//! ```
72//! use yoshi_std::{Yoshi, YoshiKind};
73//! # use std::io;
74//! # use std::io::ErrorKind;
75//! #
76//! # fn simulate_io_error() -> Result<(), io::Error> {
77//! #    Err(io::Error::new(ErrorKind::PermissionDenied, "cannot access file"))
78//! # }
79//!
80//! fn load_config(path: &str) -> Result<String, Yoshi> {
81//!     // Convert I/O errors to Yoshi errors with additional context
82//!     simulate_io_error()
83//!         .map_err(Yoshi::from)?;
84//!
85//!     // Errors can be built up with context as they propagate
86//!     Err(Yoshi::new(YoshiKind::NotFound {
87//!         resource_type: "config file".into(),
88//!         identifier: path.into(),
89//!         search_locations: None,
90//!     })
91//!     .with_metadata("config_path", path)
92//!     .with_suggestion("Ensure the configuration file exists and is readable")
93//!     .context(format!("Failed to load configuration from {}", path)))
94//! }
95//!
96//! # fn main() {
97//! match load_config("/etc/app/config.json") {
98//!     Ok(config) => println!("Loaded: {}", config),
99//!     Err(error) => {
100//!         eprintln!("Configuration error: {}", error);
101//!         // Rich error output includes context, metadata, and suggestions
102//!     }
103//! }
104//! # }
105//! ```
106//!
107//! Working with typed payloads and structured data:
108//!
109//! ```
110//! use yoshi_std::{Yoshi, YoshiKind};
111//!
112//! #[derive(Debug)]
113//! struct RequestId(String);
114//!
115//! fn process_request(id: &str) -> Result<(), Yoshi> {
116//!     Err(Yoshi::new(YoshiKind::Timeout {
117//!         operation: "database query".into(),
118//!         duration: std::time::Duration::from_secs(30),
119//!         expected_max: Some(std::time::Duration::from_secs(10)),
120//!     })
121//!     .with_shell(RequestId(id.to_string()))
122//!     .with_metadata("user_id", "12345")
123//!     .context("Request processing failed"))
124//! }
125//!
126//! # fn main() {
127//! if let Err(error) = process_request("req_001") {
128//!     // Access structured data from the error
129//!     if let Some(request_id) = error.shell::<RequestId>() {
130//!         println!("Failed request: {:?}", request_id);
131//!     }
132//!
133//!     println!("Error details: {}", error);
134//! }
135//! # }
136//! ```
137//!
138//! [`anyhow`]: https://docs.rs/anyhow
139//! [`thiserror`]: https://docs.rs/thiserror
140//!
141// ~=####====A===r===c===M===o===o===n====S===t===u===d===i===o===s====X|0|$>
142//! + Structured error handling with context preservation [O(1) error creation, O(1) context attachment]
143//!  - Type-safe error categorization with detailed diagnostic information [Memory-safe, Thread-safe]
144//!  - Context chaining for complete error trace visibility [Stack-overflow protection, bounded depth]
145//!  - Conditional backtrace capture with performance monitoring [Zero-cost when disabled]
146//!  - Memory-efficient formatting with minimal allocations [Pre-allocated buffers, shared strings]
147// ~=####====A===r===c===M===o===o===n====S===t===u===d===i===o===s====X|0|$>
148// **GitHub:** [ArcMoon Studios](https://github.com/arcmoonstudios)
149// **Copyright:** (c) 2025 ArcMoon Studios
150// **License:** MIT OR Apache-2.0
151// **License File:** /LICENSE
152// **Contact:** LordXyn@proton.me
153// **Author:** Lord Xyn
154
155// Add serde helper functions for Arc<str> serialization
156#[cfg(feature = "serde")]
157mod serde_helpers {
158    use super::String;
159    use serde::{Deserialize, Deserializer, Serialize, Serializer};
160    use std::collections::HashMap;
161    use std::sync::Arc;
162
163    /// Serialize `Option<Arc<str>>` as `Option<String>`
164    #[allow(clippy::ref_option)]
165    pub fn serialize_arc_str<S>(value: &Option<Arc<str>>, serializer: S) -> Result<S::Ok, S::Error>
166    where
167        S: Serializer,
168    {
169        value
170            .as_ref()
171            .map(std::convert::AsRef::as_ref)
172            .serialize(serializer)
173    }
174
175    /// Deserialize `Option<String>` as `Option<Arc<str>>`
176    pub fn deserialize_arc_str<'de, D>(deserializer: D) -> Result<Option<Arc<str>>, D::Error>
177    where
178        D: Deserializer<'de>,
179    {
180        let opt_string: Option<String> = Option::deserialize(deserializer)?;
181        Ok(opt_string.map(|s| Arc::from(s.as_str())))
182    }
183
184    /// Serialize `HashMap<Arc<str>, Arc<str>>` as `HashMap<String, String>`
185    pub fn serialize_arc_str_map<S>(
186        value: &HashMap<Arc<str>, Arc<str>>,
187        serializer: S,
188    ) -> Result<S::Ok, S::Error>
189    where
190        S: Serializer,
191    {
192        let string_map: HashMap<&str, &str> = value
193            .iter()
194            .map(|(k, v)| (k.as_ref(), v.as_ref()))
195            .collect();
196        string_map.serialize(serializer)
197    }
198
199    /// Deserialize `HashMap<String, String>` as `HashMap<Arc<str>, Arc<str>>`
200    pub fn deserialize_arc_str_map<'de, D>(
201        deserializer: D,
202    ) -> Result<HashMap<Arc<str>, Arc<str>>, D::Error>
203    where
204        D: Deserializer<'de>,
205    {
206        let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
207        Ok(string_map
208            .into_iter()
209            .map(|(k, v)| (Arc::from(k.as_str()), Arc::from(v.as_str())))
210            .collect())
211    }
212}
213
214#[cfg(feature = "serde")]
215use serde_helpers::{
216    deserialize_arc_str, deserialize_arc_str_map, serialize_arc_str, serialize_arc_str_map,
217};
218
219// Unconditionally enable alloc crate for no_std builds using heap allocations
220#[cfg(not(feature = "std"))]
221extern crate alloc;
222
223// Unified imports for String, Vec, Box, Arc based on 'std' feature
224#[cfg(not(feature = "std"))]
225pub use alloc::{
226    boxed::Box,
227    string::{String, ToString},
228    sync::Arc,
229    vec::Vec,
230};
231#[cfg(feature = "std")]
232pub use std::{
233    boxed::Box,
234    string::{String, ToString},
235    sync::Arc,
236    vec::Vec,
237};
238
239use core::any::Any; // Import Any for error_generic_member_access and blanket From
240use core::error::Error; // Removed Request as it's unstable
241use core::fmt::{self, Display, Formatter};
242use core::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
243use core::time::Duration;
244
245// Additional imports for advanced features
246// Unified imports for HashMap based on 'std' feature
247#[cfg(not(feature = "std"))]
248use alloc::collections::BTreeMap as HashMap;
249#[cfg(feature = "std")]
250use std::collections::HashMap; // Using BTreeMap for no_std by default
251                               // Unified imports for SystemTime and Thread based on 'std' feature
252#[cfg(feature = "std")]
253use std::{thread, time::SystemTime};
254#[cfg(not(feature = "std"))]
255/// Enhanced SystemTime for `no_std` environments with monotonic counter.
256#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
257#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
258pub struct SystemTime {
259    /// Monotonic timestamp counter for ordering events
260    timestamp: u64,
261}
262
263#[cfg(not(feature = "std"))]
264impl SystemTime {
265    /// Returns a `SystemTime` with monotonic ordering guarantees.
266    ///
267    /// While not wall-clock time, this provides ordering semantics
268    /// useful for debugging and event correlation in no_std environments.
269    pub fn now() -> Self {
270        static COUNTER: AtomicU64 = AtomicU64::new(0);
271        Self {
272            timestamp: COUNTER.fetch_add(1, Ordering::Relaxed),
273        }
274    }
275
276    /// Returns the internal timestamp for debugging purposes.
277    pub const fn timestamp(&self) -> u64 {
278        self.timestamp
279    }
280
281    /// Calculates duration since another SystemTime (in timestamp units).
282    pub const fn duration_since(&self, earlier: SystemTime) -> Option<u64> {
283        if self.timestamp >= earlier.timestamp {
284            Some(self.timestamp - earlier.timestamp)
285        } else {
286            None
287        }
288    }
289
290    /// Returns elapsed timestamp units since this SystemTime.
291    pub fn elapsed(&self) -> u64 {
292        Self::now().timestamp.saturating_sub(self.timestamp)
293    }
294}
295#[cfg(not(feature = "std"))]
296use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};
297
298#[cfg(not(feature = "std"))]
299/// Enhanced ThreadId for `no_std` environments with unique identification.
300#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
301pub struct ThreadId {
302    /// Unique identifier for tracking execution contexts
303    id: u32,
304}
305
306#[cfg(not(feature = "std"))]
307impl ThreadId {
308    /// Returns a `ThreadId` with unique identification.
309    ///
310    /// In no_std environments, this provides unique identifiers
311    /// useful for correlating errors across different execution contexts.
312    pub fn current() -> Self {
313        static THREAD_COUNTER: AtomicU32 = AtomicU32::new(1);
314
315        // Use thread-local storage pattern with atomic fallback
316        #[cfg(all(target_has_atomic = "ptr", any(feature = "std", target_thread_local)))]
317        {
318            use core::cell::Cell;
319            thread_local! {
320                static THREAD_ID: Cell<Option<u32>> = const { Cell::new(None) };
321            }
322
323            THREAD_ID.with(|id| {
324                let current_id = id.get().unwrap_or_else(|| {
325                    let new_id = THREAD_COUNTER.fetch_add(1, Ordering::Relaxed);
326                    id.set(Some(new_id));
327                    new_id
328                });
329
330                Self { id: current_id }
331            })
332        }
333        #[cfg(not(all(target_has_atomic = "ptr", any(feature = "std", target_thread_local))))]
334        {
335            // Fallback for platforms without atomic or thread_local support
336            Self {
337                id: THREAD_COUNTER.fetch_add(1, Ordering::Relaxed),
338            }
339        }
340    }
341
342    /// Returns the raw thread ID for debugging.
343    #[inline]
344    pub const fn as_u32(&self) -> u32 {
345        self.id
346    }
347
348    /// Creates a ThreadId from a raw ID (for testing/debugging).
349    pub const fn from_u32(id: u32) -> Self {
350        Self { id }
351    }
352}
353
354#[cfg(not(feature = "std"))]
355impl core::fmt::Display for ThreadId {
356    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
357        write!(f, "ThreadId({})", self.id)
358    }
359}
360
361// OnceLock is std-only, so it's only imported under std
362#[cfg(not(feature = "std"))]
363use core::cell::UnsafeCell;
364#[cfg(not(feature = "std"))]
365use core::sync::atomic::{AtomicBool, Ordering};
366#[cfg(feature = "std")]
367use std::sync::OnceLock;
368
369#[cfg(not(feature = "std"))]
370/// Thread-safe one-time initialization for `no_std` environments using atomics.
371pub struct OnceLock<T> {
372    initialized: AtomicBool,
373    data: UnsafeCell<Option<T>>,
374}
375
376#[cfg(not(feature = "std"))]
377unsafe impl<T: Send + Sync> Sync for OnceLock<T> {}
378#[cfg(not(feature = "std"))]
379unsafe impl<T: Send> Send for OnceLock<T> {}
380
381#[cfg(not(feature = "std"))]
382impl<T> OnceLock<T> {
383    /// Creates a new `OnceLock` for no_std environments.
384    pub const fn new() -> Self {
385        Self {
386            initialized: AtomicBool::new(false),
387            data: UnsafeCell::new(None),
388        }
389    }
390
391    /// Gets or initializes the value using atomic operations for thread safety.
392    pub fn get_or_init(&self, f: impl FnOnce() -> T) -> &T {
393        // Use compare_exchange for proper synchronization
394        if self
395            .initialized
396            .compare_exchange_weak(false, true, Ordering::AcqRel, Ordering::Acquire)
397            .is_ok()
398        {
399            let value = f();
400            unsafe {
401                let data_ptr = self.data.get();
402                *data_ptr = Some(value);
403            }
404        } else {
405            // Spin until initialization is complete
406            while !self.initialized.load(Ordering::Acquire) {
407                core::hint::spin_loop();
408            }
409        }
410
411        unsafe {
412            let data_ptr = self.data.get();
413            (*data_ptr).as_ref().unwrap_unchecked()
414        }
415    }
416
417    /// Gets the value if it has been initialized.
418    pub fn get(&self) -> Option<&T> {
419        if self.initialized.load(Ordering::Acquire) {
420            unsafe {
421                let data_ptr = self.data.get();
422                (*data_ptr).as_ref()
423            }
424        } else {
425            None
426        }
427    }
428}
429
430/// Enhanced wrapper for foreign errors with better context preservation
431#[derive(Debug)]
432struct ForeignErrorWrapper {
433    inner: Box<dyn Error + Send + Sync + 'static>,
434    context: String,
435    enhanced_message: String,
436}
437
438impl Display for ForeignErrorWrapper {
439    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
440        if self.context.is_empty() {
441            write!(f, "{}", self.enhanced_message)
442        } else {
443            write!(f, "{}: {}", self.context, self.enhanced_message)
444        }
445    }
446}
447
448impl Error for ForeignErrorWrapper {
449    fn source(&self) -> Option<&(dyn Error + 'static)> {
450        Some(self.inner.as_ref())
451    }
452}
453
454/// High-performance buffer for error formatting with safe optimizations
455pub struct OptimizedFormatBuffer {
456    data: String,
457    reserved_capacity: usize,
458}
459
460impl OptimizedFormatBuffer {
461    const DEFAULT_CAPACITY: usize = 2048; // 2KB optimized default
462
463    /// Creates a new optimized format buffer with default capacity.
464    ///
465    /// Initializes a new `OptimizedFormatBuffer` with a default capacity of 4KB,
466    /// which is optimized for typical error formatting scenarios. The buffer
467    /// uses intelligent growth strategies to minimize memory allocations.
468    ///
469    /// # Returns
470    ///
471    /// A new `OptimizedFormatBuffer` instance with default capacity.
472    ///
473    /// # Examples
474    ///
475    /// ```
476    /// # use yoshi_std::OptimizedFormatBuffer;
477    /// let buffer = OptimizedFormatBuffer::new();
478    /// assert_eq!(buffer.as_str(), "");
479    /// ```
480    #[must_use]
481    pub fn new() -> Self {
482        Self {
483            data: String::with_capacity(Self::DEFAULT_CAPACITY),
484            reserved_capacity: Self::DEFAULT_CAPACITY,
485        }
486    }
487}
488
489impl Default for OptimizedFormatBuffer {
490    fn default() -> Self {
491        Self::new()
492    }
493}
494
495impl OptimizedFormatBuffer {
496    /// Creates a new optimized format buffer with specified capacity.
497    ///
498    /// Initializes a new `OptimizedFormatBuffer` with a custom initial capacity.
499    /// This is useful when you have an estimate of the final formatted size
500    /// and want to avoid reallocations during formatting operations.
501    ///
502    /// # Arguments
503    ///
504    /// * `capacity` - The initial capacity for the internal string buffer.
505    ///
506    /// # Returns
507    ///
508    /// A new `OptimizedFormatBuffer` instance with the specified capacity.
509    ///
510    /// # Examples
511    ///
512    /// ```
513    /// # use yoshi_std::OptimizedFormatBuffer;
514    /// let buffer = OptimizedFormatBuffer::with_capacity(8192);
515    /// assert_eq!(buffer.as_str(), "");
516    /// ```
517    #[must_use]
518    pub fn with_capacity(capacity: usize) -> Self {
519        Self {
520            data: String::with_capacity(capacity),
521            reserved_capacity: capacity,
522        }
523    }
524
525    /// High-performance string appending with optimized growth strategy
526    pub fn append_optimized(&mut self, s: &str) {
527        let current_len = self.data.len();
528        let append_len = s.len();
529        let new_len = current_len + append_len;
530
531        // Optimized growth strategy: 1.5x growth with minimum thresholds
532        if new_len > self.data.capacity() {
533            let current_cap = self.data.capacity();
534            // Ensure minimum growth of at least reserved_capacity or 256 bytes for small buffers
535            let min_growth_needed = self.reserved_capacity.max(256);
536            let growth_target_1_5x = current_cap + (current_cap >> 1); // 1.5x growth
537            let new_capacity = growth_target_1_5x.max(new_len).max(min_growth_needed);
538
539            // Reserve exactly what we need to avoid over-allocation, but also ensure minimum
540            self.data.reserve(new_capacity - current_cap);
541        }
542
543        // Use efficient string concatenation, which is highly optimized by Rust
544        self.data.push_str(s);
545    }
546
547    /// Returns a string slice of the buffer's contents.
548    ///
549    /// This method provides read-only access to the formatted content within the buffer.
550    /// The returned string slice is guaranteed to be valid UTF-8 as all input is validated.
551    ///
552    /// # Returns
553    ///
554    /// A string slice containing the current buffer contents.
555    ///
556    /// # Examples
557    ///
558    /// ```rust
559    /// # use yoshi_std::OptimizedFormatBuffer;
560    /// let mut buffer = OptimizedFormatBuffer::new();
561    /// buffer.append_optimized("Hello, World!");
562    /// assert_eq!(buffer.as_str(), "Hello, World!");
563    /// ```
564    ///
565    /// # Performance
566    ///
567    /// This operation has O(1) time complexity and does not involve any allocations.
568    #[must_use]
569    pub fn as_str(&self) -> &str {
570        &self.data
571    }
572
573    /// Clears the buffer contents while preserving the allocated capacity.
574    ///
575    /// This method efficiently removes all content from the buffer without
576    /// deallocating the underlying storage. This allows for optimal memory reuse
577    /// when the buffer will be used again with similar content sizes.
578    ///
579    /// # Examples
580    ///
581    /// ```rust
582    /// # use yoshi_std::OptimizedFormatBuffer;
583    /// let mut buffer = OptimizedFormatBuffer::new();
584    /// buffer.append_optimized("Hello, World!");
585    /// assert_eq!(buffer.as_str().len(), 13);
586    ///
587    /// buffer.clear();
588    /// assert_eq!(buffer.as_str().len(), 0);
589    /// assert!(buffer.as_str().is_empty());
590    /// ```
591    ///
592    /// # Performance
593    ///
594    /// This operation has O(1) time complexity and preserves allocated capacity
595    /// for optimal memory reuse patterns.
596    pub fn clear(&mut self) {
597        self.data.clear();
598    }
599
600    /// Optimized formatting for multiple string fragments
601    pub fn append_multiple(&mut self, fragments: &[&str]) {
602        let total_len: usize = fragments.iter().map(|s| s.len()).sum();
603        let new_len = self.data.len() + total_len;
604
605        if new_len > self.data.capacity() {
606            let new_capacity = (new_len * 2)
607                .next_power_of_two()
608                .max(self.reserved_capacity);
609            self.data.reserve_exact(new_capacity - self.data.capacity());
610        }
611
612        for fragment in fragments {
613            self.data.push_str(fragment);
614        }
615    }
616}
617
618/// Comprehensive error recovery strategies
619#[derive(Debug, Clone)]
620pub enum ErrorRecoveryStrategy {
621    /// Retry with exponential backoff
622    ExponentialBackoff {
623        /// Initial delay before the first retry attempt
624        initial_delay: Duration,
625        /// Maximum number of retry attempts before giving up
626        max_retries: u32,
627        /// Multiplier for exponential backoff calculation (e.g., 2.0 for doubling)
628        backoff_multiplier: f64,
629    },
630    /// Retry with fixed intervals
631    FixedInterval {
632        /// Fixed time interval between retry attempts
633        interval: Duration,
634        /// Maximum number of retry attempts before giving up
635        max_retries: u32,
636    },
637    /// Fallback to alternative approach
638    Fallback {
639        /// Human-readable description of the fallback strategy
640        description: String,
641    },
642    /// Circuit breaker pattern
643    CircuitBreaker {
644        /// Number of consecutive failures before opening the circuit
645        failure_threshold: u32,
646        /// Timeout duration before attempting to close the circuit
647        recovery_timeout: Duration,
648    },
649    /// No recovery possible
650    NonRecoverable,
651}
652
653/// Detailed context analysis results
654#[derive(Debug, Default)]
655pub struct ContextAnalysis {
656    /// Total number of context objects attached to the error
657    pub total_contexts: usize,
658    /// Maximum depth of nested context information
659    pub context_depth: usize,
660    /// Whether the error includes user-facing suggestions
661    pub has_suggestions: bool,
662    /// Whether source code location information is available
663    pub has_location_info: bool,
664    /// Number of metadata key-value pairs attached
665    pub metadata_entries: usize,
666    /// Number of typed shell objects attached
667    pub typed_payloads: usize,
668    /// Priority level of the primary context (0-255)
669    pub primary_context_priority: u8,
670}
671
672/// Performance-optimized Result alias with mathematical precision guarantees.
673///
674/// This type alias simplifies the use of `Result` where the error type is
675/// fixed to [`Yoshi`]. It automatically adapts between `std::result::Result`
676/// and `core::result::Result` based on the enabled features.
677///
678/// # Examples
679///
680/// ```
681/// use yoshi_std::{Result, Yoshi, YoshiKind};
682///
683/// fn divide(a: f64, b: f64) -> Result<f64> {
684///     if b == 0.0 {
685///         return Err(Yoshi::new(YoshiKind::Validation {
686///             field: "divisor".into(),
687///             message: "Division by zero is not allowed".into(),
688///             expected: Some("non-zero".into()),
689///             actual: Some("0.0".into()),
690///         }));
691///     }
692///     Ok(a / b)
693/// }
694///
695/// let result = divide(10.0, 2.0);
696/// assert!(result.is_ok());
697/// assert_eq!(result.unwrap(), 5.0);
698///
699/// let error_result = divide(10.0, 0.0);
700/// assert!(error_result.is_err());
701/// ```
702#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
703#[cfg(feature = "std")]
704pub type Result<T, E = Yoshi> = std::result::Result<T, E>;
705#[cfg(not(feature = "std"))]
706/// Performance-optimized Result alias for `no_std` builds.
707///
708/// This type alias simplifies the use of `Result` where the error type is
709/// fixed to [`Yoshi`]. It automatically adapts between `std::result::Result`
710/// and `core::result::Result` based on the enabled features.
711///
712/// # Examples
713///
714/// ```
715/// use yoshi_std::{Result, Yoshi, YoshiKind, NoStdIo};
716///
717/// fn check_value(value: i32) -> Result<i32> {
718///     if value < 0 {
719///         return Err(Yoshi::new(YoshiKind::Validation {
720///             field: "value".into(),
721///             message: "Value cannot be negative".into(),
722///             expected: Some("non-negative".into()),
723///             actual: Some(value.to_string().into()),
724///         }));
725///     }
726///     Ok(value)
727/// }
728///
729/// let result = check_value(5);
730/// assert!(result.is_ok());
731/// assert_eq!(result.unwrap(), 5);
732///
733/// let error_result = check_value(-1);
734/// assert!(error_result.is_err());
735/// ```
736pub type Result<T, E = Yoshi> = core::result::Result<T, E>;
737
738/// Ergonomic type alias for `Result<T, Yoshi>` with thematic naming.
739///
740/// This type alias provides expressive naming that aligns with the Yoshi metaphorical
741/// framework while maintaining zero-cost abstraction guarantees. It automatically
742/// adapts between `std::result::Result` and `core::result::Result` based on features.
743///
744/// # Performance Characteristics
745///
746/// - **Time Complexity**: O(1) for all operations (zero-cost abstraction)
747/// - **Space Complexity**: Identical to `Result<T, Yoshi>` (no overhead)
748/// - **Memory Layout**: Exact same representation as standard `Result`
749///
750/// # Examples
751///
752/// ```rust
753/// use yoshi_std::{Hatch, Yoshi, YoshiKind};
754///
755/// fn load_config() -> Hatch<String> {
756///     Ok("configuration data".into())
757/// }
758///
759/// fn process_data() -> Hatch<u32> {
760///     Err(Yoshi::new(YoshiKind::Internal {
761///         message: "processing failed".into(),
762///         source: None,
763///         component: None,
764///     }))
765/// }
766/// ```
767pub type Hatch<T> = Result<T, Yoshi>;
768
769/// Global error instance counter for debugging and performance monitoring.
770///
771/// This atomic counter tracks the total number of `Yoshi` error instances
772/// that have been created since the application started. It's primarily
773/// used for performance monitoring and diagnostic purposes.
774static ERROR_INSTANCE_COUNTER: AtomicU32 = AtomicU32::new(0);
775
776/// Global string interning pool for optimal memory reuse
777static STRING_INTERN_POOL: OnceLock<StringInternPool> = OnceLock::new();
778
779/// Checks if running in production mode for security sanitization
780#[inline]
781fn is_production_mode() -> bool {
782    #[cfg(feature = "std")]
783    {
784        std::env::var("YOSHI_PRODUCTION_MODE")
785            .map(|v| v == "1" || v.to_lowercase() == "true")
786            .unwrap_or(false)
787    }
788    #[cfg(not(feature = "std"))]
789    {
790        false // Default to development mode in no_std
791    }
792}
793
794/// Sanitizes error messages to remove potentially sensitive information in production
795fn sanitize_error_message(msg: &str) -> String {
796    // Define constants first before any statements
797    const MAX_MESSAGE_LENGTH: usize = 256;
798
799    let mut sanitized = msg.to_string();
800
801    // Simple string replacement for common sensitive patterns
802    let lower_msg = msg.to_lowercase();
803    if lower_msg.contains("password") {
804        sanitized = sanitized.replace("password", "password=[REDACTED]");
805    }
806    if lower_msg.contains("token") {
807        sanitized = sanitized.replace("token", "token=[REDACTED]");
808    }
809    if lower_msg.contains("key") {
810        sanitized = sanitized.replace("key", "key=[REDACTED]");
811    }
812
813    // Truncate very long messages that might contain sensitive data dumps
814    if sanitized.len() > MAX_MESSAGE_LENGTH {
815        sanitized.truncate(MAX_MESSAGE_LENGTH);
816        sanitized.push_str("... [truncated]");
817    }
818
819    sanitized
820}
821
822/// High-performance string interning with autonomous memory management and lock-free fast paths
823struct StringInternPool {
824    #[cfg(feature = "std")]
825    pool: std::sync::RwLock<std::collections::HashMap<String, Arc<str>>>,
826    #[cfg(not(feature = "std"))]
827    pool: alloc::collections::BTreeMap<String, Arc<str>>,
828    hits: AtomicUsize,
829    misses: AtomicUsize,
830    cache_size: AtomicUsize,
831}
832
833impl StringInternPool {
834    fn new() -> Self {
835        Self {
836            #[cfg(feature = "std")]
837            pool: std::sync::RwLock::new(std::collections::HashMap::with_capacity(128)),
838            #[cfg(not(feature = "std"))]
839            pool: alloc::collections::BTreeMap::new(),
840            hits: AtomicUsize::new(0),
841            misses: AtomicUsize::new(0),
842            cache_size: AtomicUsize::new(0),
843        }
844    }
845
846    /// Clears the interning pool to prevent memory leaks in long-running applications
847    #[cfg(feature = "std")]
848    pub fn clear_pool(&self) {
849        if let Ok(mut pool) = self.pool.write() {
850            pool.clear();
851            self.cache_size.store(0, Ordering::Release);
852        }
853    }
854
855    fn intern(&self, s: impl Into<String>) -> Arc<str> {
856        let string = s.into();
857
858        // Early exit for empty strings
859        if string.is_empty() {
860            return Arc::from("");
861        }
862
863        #[cfg(feature = "std")]
864        {
865            // Fast path: check if already interned with non-blocking try_read for performance
866            if let Ok(pool) = self.pool.try_read() {
867                if let Some(interned) = pool.get(&string) {
868                    self.hits.fetch_add(1, Ordering::Relaxed);
869                    return interned.clone();
870                }
871            }
872
873            // Cache size check before expensive write lock
874            const MAX_CACHE_SIZE: usize = 512;
875            let current_size = self.cache_size.load(Ordering::Relaxed);
876            if current_size >= MAX_CACHE_SIZE {
877                // Skip interning for large caches to prevent memory bloat
878                self.misses.fetch_add(1, Ordering::Relaxed);
879                return string.into();
880            }
881
882            // Slow path: intern new string (acquire write lock)
883            let mut pool = self
884                .pool
885                .write()
886                .unwrap_or_else(std::sync::PoisonError::into_inner);
887
888            // Double-check pattern (after acquiring write lock, for race conditions during read)
889            if let Some(interned) = pool.get(&string) {
890                self.hits.fetch_add(1, Ordering::Relaxed);
891                return interned.clone();
892            }
893
894            let current_pool_size = pool.len();
895            if current_pool_size < MAX_CACHE_SIZE {
896                let arc_str: Arc<str> = string.as_str().into();
897                pool.insert(string, arc_str.clone());
898                self.cache_size
899                    .store(current_pool_size + 1, Ordering::Release);
900                self.misses.fetch_add(1, Ordering::Relaxed);
901                arc_str
902            } else {
903                // Cache is full, return without interning
904                self.cache_size.store(current_pool_size, Ordering::Release);
905                self.misses.fetch_add(1, Ordering::Relaxed);
906                string.into()
907            }
908        }
909
910        #[cfg(not(feature = "std"))]
911        {
912            // High-performance lock-free string interning using separate chaining with explicit capacity management
913            use core::ptr;
914            use core::sync::atomic::AtomicPtr;
915
916            // Fixed-size lock-free cache with atomic slots (larger for fewer collisions)
917            const CACHE_SLOTS: usize = 256; // Power of 2 for efficient modulo
918            static CACHE: [AtomicPtr<CacheEntry>; CACHE_SLOTS] =
919                [const { AtomicPtr::new(ptr::null_mut()) }; CACHE_SLOTS];
920
921            // Global maximum number of interned strings to prevent unbounded memory growth in no_std
922            const MAX_GLOBAL_CACHE_SIZE: usize = 512;
923
924            #[repr(C)]
925            struct CacheEntry {
926                hash: u64,
927                arc_str: Arc<str>,
928                next: AtomicPtr<CacheEntry>,
929            }
930
931            // Fast hash function for cache slot selection (FNV-1a)
932            #[inline(always)] // Ensure inlining for performance-critical path
933            fn fast_hash(s: &str) -> u64 {
934                let mut hash = 0xcbf29ce484222325u64; // FNV-1a offset basis
935                for byte in s.bytes() {
936                    hash ^= byte as u64;
937                    hash = hash.wrapping_mul(0x100000001b3u64); // FNV-1a prime
938                }
939                hash
940            }
941
942            let hash = fast_hash(&string);
943            let slot_index = (hash as usize) & (CACHE_SLOTS - 1); // Efficient modulo for power of 2
944
945            // Lock-free search in the cache slot's linked list
946            let mut current = CACHE[slot_index].load(Ordering::Acquire);
947            while !current.is_null() {
948                unsafe {
949                    let entry = &*current;
950                    if entry.hash == hash && entry.arc_str.as_ref() == string {
951                        self.hits.fetch_add(1, Ordering::Relaxed);
952                        return entry.arc_str.clone();
953                    }
954                    current = entry.next.load(Ordering::Acquire);
955                }
956            }
957
958            // Cache miss: attempt to increment global cache size *before* allocation
959            let new_cache_size = self.cache_size.fetch_add(1, Ordering::Relaxed) + 1;
960            if new_cache_size > MAX_GLOBAL_CACHE_SIZE {
961                // If over capacity, decrement counter (to prevent false overflow) and return original string
962                self.cache_size.fetch_sub(1, Ordering::Relaxed); // Correct the increment
963                self.misses.fetch_add(1, Ordering::Relaxed);
964                return string.into(); // Return uninterned string
965            }
966
967            let arc_str: Arc<str> = string.into(); // Allocate string
968            let new_entry = Box::into_raw(Box::new(CacheEntry {
969                hash,
970                arc_str: arc_str.clone(),
971                next: AtomicPtr::new(ptr::null_mut()),
972            }));
973
974            // Atomic compare-and-swap insertion at head of linked list
975            let mut head = CACHE[slot_index].load(Ordering::Acquire);
976            loop {
977                unsafe {
978                    (*new_entry).next.store(head, Ordering::Relaxed);
979                }
980
981                match CACHE[slot_index].compare_exchange_weak(
982                    head,
983                    new_entry,
984                    Ordering::Release,
985                    Ordering::Acquire,
986                ) {
987                    Ok(_) => {
988                        // Successfully inserted new entry
989                        self.misses.fetch_add(1, Ordering::Relaxed);
990                        return arc_str;
991                    }
992                    Err(current_head) => {
993                        // Another thread modified the head, retry with new head
994                        head = current_head;
995
996                        // Double-check if another thread inserted our string
997                        let mut search_current = head;
998                        while !search_current.is_null() {
999                            unsafe {
1000                                let entry = &*search_current;
1001                                if entry.hash == hash && entry.arc_str.as_ref() == string {
1002                                    // Another thread inserted our string, clean up and return
1003                                    let _ = unsafe { Box::from_raw(new_entry) }; // Clean up unused entry
1004                                    self.hits.fetch_add(1, Ordering::Relaxed);
1005                                    self.cache_size.fetch_sub(1, Ordering::Relaxed); // Correct the size
1006                                    return entry.arc_str.clone();
1007                                }
1008                                search_current = entry.next.load(Ordering::Acquire);
1009                            }
1010                        }
1011                        // Continue loop to retry insertion
1012                    }
1013                }
1014            }
1015        }
1016    }
1017
1018    /// Returns (hits, misses) for performance monitoring
1019    #[inline]
1020    pub fn stats(&self) -> (usize, usize) {
1021        (
1022            self.hits.load(Ordering::Relaxed),
1023            self.misses.load(Ordering::Relaxed),
1024        )
1025    }
1026
1027    /// Returns current cache size for autonomous memory monitoring
1028    #[inline]
1029    pub fn cache_size(&self) -> usize {
1030        self.cache_size.load(Ordering::Acquire)
1031    }
1032}
1033
1034/// Optimized string interning function
1035#[inline]
1036pub fn intern_string(s: impl Into<String>) -> Arc<str> {
1037    STRING_INTERN_POOL
1038        .get_or_init(StringInternPool::new)
1039        .intern(s)
1040}
1041
1042/// Gets the current number of Yoshi error instances created.
1043///
1044/// This function provides a way to inspect the cumulative count of `Yoshi`
1045/// error objects instantiated. It can be useful for profiling, detecting
1046/// excessive error creation, or understanding error patterns in an
1047/// application.
1048///
1049/// # Returns
1050///
1051/// The total number of `Yoshi` error instances created as a `u64`.
1052///
1053/// # Examples
1054///
1055/// ```
1056/// use yoshi_std::{Yoshi, YoshiKind, error_instance_count};
1057///
1058/// let initial_count = error_instance_count();
1059/// let _err1 = Yoshi::new(YoshiKind::Internal {
1060///     message: "simulated error 1".into(),
1061///     source: None,
1062///     component: None,
1063/// });
1064/// let _err2 = Yoshi::new(YoshiKind::Internal {
1065///     message: "simulated error 2".into(),
1066///     source: None,
1067///     component: None,
1068/// });
1069///
1070/// assert_eq!(error_instance_count(), initial_count + 2);
1071/// ```
1072pub fn error_instance_count() -> u32 {
1073    ERROR_INSTANCE_COUNTER.load(Ordering::Relaxed)
1074}
1075
1076/// Resets the global error instance counter.
1077///
1078/// This function is intended primarily for use in test environments
1079/// to ensure test isolation and predictable counter values.
1080/// It should **not** be used in production code.
1081#[cfg(test)]
1082#[inline]
1083pub fn reset_error_instance_counter() {
1084    ERROR_INSTANCE_COUNTER.store(0, Ordering::Relaxed);
1085}
1086
1087//--------------------------------------------------------------------------------------------------
1088// Enhanced NoStdIo with performance optimization
1089//--------------------------------------------------------------------------------------------------
1090
1091#[cfg(not(feature = "std"))]
1092/// Structured error kinds for better type safety in no_std I/O operations.
1093#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1094pub enum NoStdIoKind {
1095    /// A file or directory was not found.
1096    NotFound,
1097    /// Permission was denied for the operation.
1098    PermissionDenied,
1099    /// A network connection was refused.
1100    ConnectionRefused,
1101    /// An operation timed out.
1102    TimedOut,
1103    /// A generic I/O error occurred.
1104    Generic,
1105    /// Other error types not covered by specific variants.
1106    Other,
1107}
1108
1109#[cfg(not(feature = "std"))]
1110impl NoStdIoKind {
1111    /// Returns a human-readable description of the error kind.
1112    pub const fn as_str(&self) -> &'static str {
1113        match self {
1114            Self::NotFound => "not found",
1115            Self::PermissionDenied => "permission denied",
1116            Self::ConnectionRefused => "connection refused",
1117            Self::TimedOut => "timed out",
1118            Self::Generic => "I/O error",
1119            Self::Other => "other error",
1120        }
1121    }
1122
1123    /// Returns whether this error kind typically indicates a transient condition.
1124    pub const fn is_transient(&self) -> bool {
1125        matches!(
1126            self,
1127            Self::ConnectionRefused | Self::TimedOut | Self::Generic
1128        )
1129    }
1130
1131    /// Returns a severity level for this error kind (0-100).
1132    pub const fn severity(&self) -> u8 {
1133        match self {
1134            Self::NotFound => 30,
1135            Self::PermissionDenied => 50,
1136            Self::ConnectionRefused => 40,
1137            Self::TimedOut => 35,
1138            Self::Generic => 45,
1139            Self::Other => 40,
1140        }
1141    }
1142}
1143
1144#[cfg(not(feature = "std"))]
1145impl core::fmt::Display for NoStdIoKind {
1146    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1147        f.write_str(self.as_str())
1148    }
1149}
1150
1151/// High-performance minimal wrapper for I/O errors in `no_std` contexts.
1152///
1153/// This enum provides a compact representation for common I/O errors
1154/// when the standard library's `std::io::Error` is not available.
1155/// It uses `Arc<str>` for message storage to minimize allocations
1156/// when messages are repeated or shared.
1157#[cfg(not(feature = "std"))]
1158#[derive(Debug, Clone)]
1159#[cfg_attr(docsrs, doc(cfg(not(feature = "std"))))]
1160pub enum NoStdIo {
1161    /// Generic I/O error with optimized string storage.
1162    GenericIo(Arc<str>),
1163    /// Indicates that a file or directory was not found.
1164    NotFound,
1165    /// Indicates that permission was denied for an operation.
1166    PermissionDenied,
1167    /// Indicates that a network connection was refused.
1168    ConnectionRefused,
1169    /// Indicates that an operation timed out.
1170    TimedOut,
1171    /// Other I/O errors, with a custom message.
1172    Other(Arc<str>),
1173}
1174
1175#[cfg(not(feature = "std"))]
1176impl NoStdIo {
1177    /// Creates a new I/O error with comprehensive categorization.
1178    ///
1179    /// This constructor attempts to categorize the error message into specific
1180    /// variants using pattern matching on common error strings, enabling
1181    /// better programmatic error handling even in no_std environments.
1182    ///
1183    /// # Arguments
1184    ///
1185    /// * `message` - A message describing the I/O error. This can be any type
1186    ///   that converts into a `String`.
1187    ///
1188    /// # Returns
1189    ///
1190    /// A new `NoStdIo` error instance.
1191    ///
1192    /// # Examples
1193    ///
1194    /// ```
1195    /// # use yoshi_std::NoStdIo;
1196    /// let err1 = NoStdIo::new("file not found");
1197    /// assert!(matches!(err1, NoStdIo::NotFound));
1198    ///
1199    /// let err2 = NoStdIo::new("disk full");
1200    /// assert!(matches!(err2, NoStdIo::Other(_)));
1201    /// ```
1202    pub fn new(message: impl Into<String>) -> Self {
1203        let msg = message.into();
1204        let lower_msg = msg.to_lowercase();
1205
1206        // Comprehensive pattern matching for better error categorization
1207        match lower_msg.as_str() {
1208            // File/resource not found patterns
1209            s if s.contains("not found")
1210                || s.contains("no such file")
1211                || s.contains("enoent")
1212                || s.contains("file does not exist") =>
1213            {
1214                Self::NotFound
1215            }
1216
1217            // Permission/access denied patterns
1218            s if s.contains("permission denied")
1219                || s.contains("access denied")
1220                || s.contains("access is denied")
1221                || s.contains("eacces")
1222                || s.contains("unauthorized")
1223                || s.contains("forbidden") =>
1224            {
1225                Self::PermissionDenied
1226            }
1227
1228            // Network connection patterns
1229            s if s.contains("connection refused")
1230                || s.contains("econnrefused")
1231                || s.contains("no route to host")
1232                || s.contains("network unreachable") =>
1233            {
1234                Self::ConnectionRefused
1235            }
1236
1237            // Timeout patterns
1238            s if s.contains("timed out")
1239                || s.contains("timeout")
1240                || s.contains("etimedout")
1241                || s.contains("operation timeout") =>
1242            {
1243                Self::TimedOut
1244            }
1245
1246            // Generic I/O patterns
1247            s if s.contains("i/o error")
1248                || s.contains("io error")
1249                || s.contains("input/output error") =>
1250            {
1251                Self::GenericIo(msg.into())
1252            }
1253
1254            // Catch-all for unrecognized patterns
1255            _ => Self::Other(msg.into()),
1256        }
1257    }
1258
1259    /// Creates a new I/O error from an error code and message.
1260    ///
1261    /// This method provides more precise error categorization when
1262    /// both an error code and message are available.
1263    pub fn from_code_and_message(code: i32, message: impl Into<String>) -> Self {
1264        match code {
1265            2 | -2 => Self::NotFound,                         // ENOENT
1266            13 | -13 => Self::PermissionDenied,               // EACCES
1267            111 | -111 => Self::ConnectionRefused,            // ECONNREFUSED
1268            110 | -110 => Self::TimedOut,                     // ETIMEDOUT
1269            5 | -5 => Self::GenericIo(message.into().into()), // EIO
1270            _ => Self::Other(message.into().into()),
1271        }
1272    }
1273
1274    /// Creates a typed I/O error for specific common scenarios.
1275    pub fn typed_error(error_type: NoStdIoKind, message: impl Into<String>) -> Self {
1276        match error_type {
1277            NoStdIoKind::NotFound => Self::NotFound,
1278            NoStdIoKind::PermissionDenied => Self::PermissionDenied,
1279            NoStdIoKind::ConnectionRefused => Self::ConnectionRefused,
1280            NoStdIoKind::TimedOut => Self::TimedOut,
1281            NoStdIoKind::Generic => Self::GenericIo(message.into().into()),
1282            NoStdIoKind::Other => Self::Other(message.into().into()),
1283        }
1284    }
1285}
1286
1287#[cfg(not(feature = "std"))]
1288impl Display for NoStdIo {
1289    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
1290        match self {
1291            Self::GenericIo(s) => write!(f, "I/O error (no_std): {s}"),
1292            Self::NotFound => f.write_str("I/O error (no_std): not found"),
1293            Self::PermissionDenied => f.write_str("I/O error (no_std): permission denied"),
1294            Self::ConnectionRefused => f.write_str("I/O error (no_std): connection refused"),
1295            Self::TimedOut => f.write_str("I/O error (no_std): timed out"),
1296            Self::Other(s) => write!(f, "I/O error (no_std): {s}"),
1297        }
1298    }
1299}
1300
1301#[cfg(not(feature = "std"))]
1302impl Error for NoStdIo {}
1303
1304//--------------------------------------------------------------------------------------------------
1305// Enhanced YoshiKind with performance optimization
1306//--------------------------------------------------------------------------------------------------
1307
1308/// High‑level categories for recoverable failures with performance optimizations.
1309///
1310/// This enum represents the fundamental classification of an error within the
1311/// `Yoshi` framework. Each variant provides specific fields relevant to its
1312/// error category, enabling rich, structured error reporting and programmatic
1313/// error handling.
1314#[derive(Debug)]
1315#[non_exhaustive]
1316pub enum YoshiKind {
1317    /// Standard I/O failure with optimized error representation.
1318    ///    /// This variant wraps `std::io::Error` when the `std` feature is enabled,
1319    /// or `NoStdIo` for `no_std` environments.
1320    #[cfg(feature = "std")]
1321    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
1322    Io(std::io::Error),
1323    /// I/O failure in `no_std` with enhanced error categorization.
1324    ///
1325    /// This variant wraps [`NoStdIo`] when the `std` feature is not enabled.
1326    #[cfg(not(feature = "std"))]
1327    #[cfg_attr(docsrs, doc(cfg(not(feature = "std"))))]
1328    Io(NoStdIo),
1329    /// Network-related error with connection and protocol context.
1330    ///
1331    /// This variant represents errors that occur during network operations,
1332    /// including connectivity issues, protocol errors, and communication failures.
1333    ///
1334    /// # Fields
1335    ///
1336    /// * `message` - A human-readable description of the network error
1337    /// * `source` - An optional nested [`Yoshi`] error that caused this network issue
1338    /// * `error_code` - An optional numeric error code from the underlying network layer
1339    ///
1340    /// # Examples
1341    ///
1342    /// ```
1343    /// # use std::sync::Arc;
1344    /// # use yoshi_std::{Yoshi, YoshiKind};
1345    /// let network_error = YoshiKind::Network {
1346    ///     message: Arc::from("Connection refused"),
1347    ///     source: None,
1348    ///     error_code: Some(111),
1349    /// };
1350    /// ```
1351    Network {
1352        /// A human-readable description of the network error.
1353        message: Arc<str>,
1354        /// An optional nested [`Yoshi`] error that caused this network issue.
1355        source: Option<Box<Yoshi>>,
1356        /// An optional network-specific error code (e.g., HTTP status code).
1357        error_code: Option<u32>,
1358    },
1359    /// Configuration error with enhanced diagnostics.
1360    ///
1361    /// Fields:
1362    /// - `message`: A human-readable description of the configuration error.
1363    /// - `source`: An optional nested `Yoshi` error that caused this configuration issue.
1364    /// - `config_path`: An optional path to the configuration file or source.
1365    Config {
1366        /// A human-readable description of the configuration error.
1367        message: Arc<str>,
1368        /// An optional nested [`Yoshi`] error that caused this configuration issue.
1369        source: Option<Box<Yoshi>>,
1370        /// An optional path to the configuration file or source.
1371        config_path: Option<Arc<str>>,
1372    },
1373    /// Data validation failure with field-level precision.
1374    ///
1375    /// Fields:
1376    /// - `field`: The name of the field that failed validation.
1377    /// - `message`: A description of why the validation failed.
1378    /// - `expected`: An optional description of the expected value or format.
1379    /// - `actual`: An optional string representation of the actual value received.
1380    Validation {
1381        /// The name of the field that failed validation.
1382        field: Arc<str>,
1383        /// A description of why the validation failed.
1384        message: Arc<str>,
1385        /// An optional description of the expected value or format.
1386        expected: Option<Arc<str>>,
1387        /// An optional string representation of the actual value received.
1388        actual: Option<Arc<str>>,
1389    },
1390    /// Internal invariant breakage with debugging context.
1391    ///
1392    /// This typically indicates a bug within the application's own logic
1393    /// or an unexpected state.
1394    ///
1395    /// Fields:
1396    /// - `message`: A description of the internal error.
1397    /// - `source`: An optional nested `Yoshi` error that caused this internal issue.
1398    /// - `component`: An optional name of the component where the error occurred.
1399    Internal {
1400        /// A description of the internal error.
1401        message: Arc<str>,
1402        /// An optional nested [`Yoshi`] error that caused this internal issue.
1403        source: Option<Box<Yoshi>>,
1404        /// An optional name of the component where the error occurred.
1405        component: Option<Arc<str>>,
1406    },
1407    /// Resource not found with typed identification.
1408    ///
1409    /// Fields:
1410    /// - `resource_type`: The type of resource (e.g., "User", "Product", "File").
1411    /// - `identifier`: The specific identifier of the resource that was not found.
1412    /// - `search_locations`: Optional list of locations where the resource was searched.
1413    NotFound {
1414        /// The type of resource (e.g., "User", "Product", "File").
1415        resource_type: Arc<str>,
1416        /// The specific identifier of the resource that was not found.
1417        identifier: Arc<str>,
1418        /// Optional list of locations where the resource was searched.
1419        search_locations: Option<Vec<Arc<str>>>,
1420    },
1421    /// Operation timeout with detailed timing information.
1422    ///
1423    /// Fields:
1424    /// - `operation`: A description of the operation that timed out.
1425    /// - `duration`: The duration for which the operation ran before timing out.
1426    /// - `expected_max`: An optional maximum expected duration for the operation.
1427    Timeout {
1428        /// A description of the operation that timed out.
1429        operation: Arc<str>,
1430        /// The duration for which the operation ran before timing out.
1431        duration: Duration,
1432        /// An optional maximum expected duration for the operation.
1433        expected_max: Option<Duration>,
1434    },
1435    /// Resource exhaustion with precise metrics.
1436    ///
1437    /// This indicates that a system resource (e.g., memory, CPU, disk space)
1438    /// has been exhausted.
1439    ///
1440    /// Fields:
1441    /// - `resource`: The type of resource exhausted (e.g., "memory", "thread pool").
1442    /// - `limit`: The configured limit for the resource.
1443    /// - `current`: The current usage or allocation of the resource when exhaustion occurred.
1444    /// - `usage_percentage`: Optional percentage of resource usage at the time of error.
1445    ResourceExhausted {
1446        /// The type of resource exhausted (e.g., "memory", "thread pool").
1447        resource: Arc<str>,
1448        /// The configured limit for the resource.
1449        limit: Arc<str>,
1450        /// The current usage or allocation of the resource when exhaustion occurred.
1451        current: Arc<str>,
1452        /// Optional percentage of resource usage at the time of error.
1453        usage_percentage: Option<f64>,
1454    },
1455    /// Foreign error wrapper with enhanced type information.
1456    ///
1457    /// This variant allows wrapping any type that implements `std::error::Error`,
1458    /// providing a uniform way to integrate external error types into the `Yoshi`
1459    /// framework.
1460    ///
1461    /// Fields:
1462    /// - `error`: The boxed foreign error object.
1463    /// - `error_type_name`: The fully qualified type name of the original error.
1464    Foreign {
1465        /// The boxed foreign error object.
1466        error: Box<dyn Error + Send + Sync + 'static>,
1467        /// The fully qualified type name of the original error.
1468        error_type_name: Arc<str>,
1469    },
1470    /// Multiple errors with categorization and priority.
1471    ///
1472    /// This variant can be used to aggregate several errors into a single `Yoshi`
1473    /// instance, useful for scenarios like batch processing or validation where
1474    /// multiple failures can occur.
1475    ///
1476    /// Fields:
1477    /// - `errors`: A vector of nested `Yoshi` errors.
1478    /// - `primary_index`: An optional index indicating which error in the `errors`
1479    ///   vector should be considered the primary error.
1480    Multiple {
1481        /// A vector of nested [`Yoshi`] errors.
1482        errors: Vec<Yoshi>,
1483        /// An optional index indicating which error in the `errors`
1484        /// vector should be considered the primary error.
1485        primary_index: Option<usize>,
1486    },
1487}
1488
1489impl YoshiKind {
1490    /// Enhanced foreign error conversion with better type preservation and sanitization
1491    pub fn from_foreign_with_context<E>(error: E, context: impl Into<String>) -> Self
1492    where
1493        E: Error + Send + Sync + 'static,
1494    {
1495        let type_name = core::any::type_name::<E>();
1496        let error_msg = error.to_string();
1497        // Apply sanitization to protect sensitive information
1498        let enhanced_msg = sanitize_error_message(&error_msg);
1499
1500        Self::Foreign {
1501            error: Box::new(ForeignErrorWrapper {
1502                inner: Box::new(error),
1503                context: context.into(),
1504                enhanced_message: enhanced_msg,
1505            }),
1506            error_type_name: intern_string(type_name),
1507        }
1508    }
1509
1510    /// Gets the severity level of this error kind (0-100, higher is more severe).
1511    ///
1512    /// This method provides a numerical indication of how critical an error
1513    /// is, allowing for programmatic decision-making based on severity
1514    /// (e.g., logging level, alerting, retry behavior).
1515    ///
1516    /// # Returns
1517    ///
1518    /// A `u8` value representing the severity, where 0 is least severe
1519    /// and 100 is most severe.
1520    ///
1521    /// # Examples
1522    ///
1523    /// ```
1524    /// # use yoshi_std::YoshiKind;
1525    /// let internal_error = YoshiKind::Internal {
1526    ///     message: "simulated error".into(),
1527    ///     source: None,
1528    ///     component: None,
1529    /// };
1530    /// assert_eq!(internal_error.severity(), 80);
1531    ///
1532    /// let validation_error = YoshiKind::Validation {
1533    ///     field: "email".into(),
1534    ///     message: "Invalid format".into(),
1535    ///     expected: None,
1536    ///     actual: None,
1537    /// };
1538    /// assert_eq!(validation_error.severity(), 20);
1539    /// ```
1540    #[must_use]
1541    pub const fn severity(&self) -> u8 {
1542        match self {
1543            #[cfg(feature = "std")]
1544            Self::Io(_) => 40,
1545            #[cfg(not(feature = "std"))]
1546            Self::Io(_) => 40,
1547            Self::Network { .. } => 50,
1548            Self::Config { .. } => 30,
1549            Self::Validation { .. } => 20,
1550            Self::Internal { .. } => 80,
1551            Self::NotFound { .. } => 25,
1552            Self::Timeout { .. } => 45,
1553            Self::ResourceExhausted { .. } => 70,
1554            Self::Foreign { .. } => 60,
1555            Self::Multiple { .. } => 65,
1556        }
1557    }
1558
1559    /// Checks if this error kind represents a transient (retryable) error.
1560    ///
1561    /// Transient errors are typically temporary issues that might resolve
1562    /// themselves if the operation is retried after a short delay (e.g.,
1563    /// network glitches, temporary resource unavailability).
1564    ///
1565    /// # Returns
1566    ///
1567    /// `true` if the error is considered transient, `false` otherwise.
1568    ///
1569    /// # Examples
1570    ///
1571    /// ```
1572    /// # use yoshi_std::YoshiKind;
1573    /// # use core::time::Duration;
1574    /// let timeout_error = YoshiKind::Timeout {
1575    ///     operation: "API call".into(),
1576    ///     duration: Duration::from_secs(10),
1577    ///     expected_max: None,
1578    /// };
1579    /// assert!(timeout_error.is_transient());
1580    ///
1581    /// let config_error = YoshiKind::Config {
1582    ///     message: "Missing key".into(),
1583    ///     source: None,
1584    ///     config_path: None,
1585    /// };
1586    /// assert!(!config_error.is_transient());
1587    /// ```
1588    #[must_use]
1589    pub const fn is_transient(&self) -> bool {
1590        matches!(
1591            self,
1592            Self::Network { .. } | Self::Timeout { .. } | Self::ResourceExhausted { .. }
1593        )
1594    }
1595}
1596
1597impl Clone for YoshiKind {
1598    fn clone(&self) -> Self {
1599        match self {
1600            #[cfg(feature = "std")]
1601            Self::Io(e) => {
1602                // std::io::Error doesn't implement Clone, recreate with same kind and message
1603                Self::Io(std::io::Error::new(e.kind(), e.to_string()))
1604            }
1605            #[cfg(not(feature = "std"))]
1606            Self::Io(e) => Self::Io(e.clone()),
1607            Self::Network {
1608                message,
1609                source,
1610                error_code,
1611            } => Self::Network {
1612                message: message.clone(),
1613                source: source.clone(),
1614                error_code: *error_code,
1615            },
1616            Self::Config {
1617                message,
1618                source,
1619                config_path,
1620            } => Self::Config {
1621                message: message.clone(),
1622                source: source.clone(),
1623                config_path: config_path.clone(),
1624            },
1625            Self::Validation {
1626                field,
1627                message,
1628                expected,
1629                actual,
1630            } => Self::Validation {
1631                field: field.clone(),
1632                message: message.clone(),
1633                expected: expected.clone(),
1634                actual: actual.clone(),
1635            },
1636            Self::Internal {
1637                message,
1638                source,
1639                component,
1640            } => Self::Internal {
1641                message: message.clone(),
1642                source: source.clone(),
1643                component: component.clone(),
1644            },
1645            Self::NotFound {
1646                resource_type,
1647                identifier,
1648                search_locations,
1649            } => Self::NotFound {
1650                resource_type: resource_type.clone(),
1651                identifier: identifier.clone(),
1652                search_locations: search_locations.clone(),
1653            },
1654            Self::Timeout {
1655                operation,
1656                duration,
1657                expected_max,
1658            } => Self::Timeout {
1659                operation: operation.clone(),
1660                duration: *duration,
1661                expected_max: *expected_max,
1662            },
1663            Self::ResourceExhausted {
1664                resource,
1665                limit,
1666                current,
1667                usage_percentage,
1668            } => Self::ResourceExhausted {
1669                resource: resource.clone(),
1670                limit: limit.clone(),
1671                current: current.clone(),
1672                usage_percentage: *usage_percentage,
1673            },
1674            Self::Foreign {
1675                error,
1676                error_type_name,
1677            } => {
1678                // Foreign errors can't be cloned directly, create a new one with same message
1679                Self::Internal {
1680                    message: format!("Cloned foreign error: {error}").into(),
1681                    source: None,
1682                    component: Some(format!("Originally: {error_type_name}").into()),
1683                }
1684            }
1685            Self::Multiple {
1686                errors,
1687                primary_index,
1688            } => Self::Multiple {
1689                errors: errors.clone(),
1690                primary_index: *primary_index,
1691            },
1692        }
1693    }
1694}
1695
1696impl Display for YoshiKind {
1697    /// Formats the `YoshiKind` for display.
1698    ///
1699    /// This implementation provides a human-readable string representation
1700    /// of the error kind, including relevant details from its fields.
1701    ///
1702    /// # Arguments
1703    ///
1704    /// * `f` - The formatter to write into.
1705    ///
1706    /// # Returns
1707    ///
1708    /// A `fmt::Result` indicating success or failure of the formatting.
1709    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1710        match self {
1711            #[cfg(feature = "std")]
1712            Self::Io(e) => write!(f, "I/O error: {e}"),
1713            #[cfg(not(feature = "std"))]
1714            Self::Io(e) => write!(f, "{e}"),
1715            Self::Network {
1716                message,
1717                error_code,
1718                ..
1719            } => {
1720                if let Some(code) = error_code {
1721                    write!(f, "Network error (code {code}): {message}")
1722                } else {
1723                    write!(f, "Network error: {message}")
1724                }
1725            }
1726            Self::Config {
1727                message,
1728                config_path,
1729                ..
1730            } => {
1731                if let Some(path) = config_path.as_ref() {
1732                    // use as_ref() for Option<Arc<str>>
1733                    write!(f, "Configuration error in '{path}': {message}")
1734                } else {
1735                    write!(f, "Configuration error: {message}")
1736                }
1737            }
1738            Self::Validation {
1739                field,
1740                message,
1741                expected,
1742                actual,
1743            } => {
1744                write!(f, "Validation error for '{field}': {message}")?;
1745                if let (Some(exp), Some(act)) = (expected, actual) {
1746                    write!(f, " (expected: {exp}, actual: {act})")?;
1747                }
1748                Ok(())
1749            }
1750            Self::Internal {
1751                message, component, ..
1752            } => {
1753                if let Some(comp) = component.as_ref() {
1754                    // use as_ref() for Option<Arc<str>>
1755                    write!(f, "Internal error in {comp}: {message}")
1756                } else {
1757                    write!(f, "Internal error: {message}")
1758                }
1759            }
1760            Self::NotFound {
1761                resource_type,
1762                identifier,
1763                ..
1764            } => write!(f, "{resource_type} not found: {identifier}"),
1765            Self::Timeout {
1766                operation,
1767                duration,
1768                expected_max,
1769            } => {
1770                write!(f, "Operation '{operation}' timed out after {duration:?}")?;
1771                if let Some(max) = expected_max {
1772                    write!(f, " (max expected: {max:?})")?;
1773                }
1774                Ok(())
1775            }
1776            Self::ResourceExhausted {
1777                resource,
1778                limit,
1779                current,
1780                usage_percentage,
1781            } => {
1782                write!(
1783                    f,
1784                    "Resource '{resource}' exhausted: {current} (limit: {limit})"
1785                )?;
1786                if let Some(pct) = usage_percentage {
1787                    write!(f, " [{pct:.1}% usage]")?;
1788                }
1789                Ok(())
1790            }
1791            Self::Foreign {
1792                error,
1793                error_type_name,
1794            } => {
1795                write!(f, "{error_type_name}: {error}")
1796            }
1797            Self::Multiple {
1798                errors,
1799                primary_index,
1800            } => {
1801                let primary = primary_index.and_then(|i| errors.get(i)); // `i` is usize, no deref needed
1802                write!(f, "Multiple errors ({} total)", errors.len())?;
1803                if let Some(primary_err) = primary {
1804                    write!(f, " - Primary: {primary_err}")?;
1805                }
1806                Ok(())
1807            }
1808        }
1809    }
1810}
1811
1812impl YoshiKind {
1813    /// Returns the underlying source of the error, if any.
1814    ///
1815    /// This method is part of the `std::error::Error` trait's contract,
1816    /// allowing for recursive traversal of error causes.
1817    ///
1818    /// # Returns
1819    ///
1820    /// An `Option` containing a reference to the underlying error that
1821    /// caused this `YoshiKind`, or `None` if there is no direct source.
1822    /// The returned reference is a trait object `&(dyn Error + 'static)`.
1823    ///    /// # Examples
1824    ///
1825    /// ```
1826    /// # use yoshi_std::{Yoshi, YoshiKind};
1827    /// # use std::io;
1828    /// # use std::io::ErrorKind;
1829    /// # use std::error::Error;
1830    /// let io_err = io::Error::new(ErrorKind::PermissionDenied, "access denied");
1831    /// let yoshi_err = Yoshi::from(io_err);
1832    ///
1833    /// // Access the source from YoshiKind using Error trait
1834    /// if let Some(source) = yoshi_err.kind().source() {
1835    ///     assert_eq!(source.to_string(), "access denied");
1836    /// } else {
1837    ///     panic!("Expected an IO error source");
1838    /// }
1839    /// ```
1840    fn source(&self) -> Option<&(dyn Error + 'static)> {
1841        match self {
1842            #[cfg(feature = "std")]
1843            Self::Io(e) => Some(e),
1844            #[cfg(not(feature = "std"))]
1845            Self::Io(e) => Some(e),
1846            Self::Network {
1847                source: Some(s), ..
1848            }
1849            | Self::Config {
1850                source: Some(s), ..
1851            }
1852            | Self::Internal {
1853                source: Some(s), ..
1854            } => Some(s.as_ref()),
1855            Self::Foreign { error, .. } => Some(error.as_ref()),
1856            Self::Multiple {
1857                errors,
1858                primary_index,
1859            } => {
1860                if let Some(idx) = primary_index {
1861                    errors.get(*idx).map(|e| e as &dyn Error)
1862                } else {
1863                    errors.first().map(|e| e as &dyn Error)
1864                }
1865            }
1866            _ => None,
1867        }
1868    }
1869}
1870
1871//--------------------------------------------------------------------------------------------------
1872// Error trait implementation for YoshiKind
1873//--------------------------------------------------------------------------------------------------
1874
1875#[cfg(feature = "std")]
1876impl Error for YoshiKind {
1877    /// Returns the underlying source of the error, if any.
1878    ///
1879    /// This method delegates to the internal `source` method, enabling
1880    /// `YoshiKind` to participate in Rust's standard error chaining mechanism.
1881    ///
1882    /// # Returns
1883    ///
1884    /// An `Option` containing a reference to the underlying error that
1885    /// caused this `YoshiKind`, or `None` if there is no direct source.
1886    fn source(&self) -> Option<&(dyn Error + 'static)> {
1887        self.source()
1888    }
1889}
1890
1891#[cfg(not(feature = "std"))]
1892impl Error for YoshiKind {
1893    /// Returns the underlying source of the error, if any.
1894    ///
1895    /// This method delegates to the internal `source` method, enabling
1896    /// `YoshiKind` to participate in Rust's standard error chaining mechanism.
1897    ///
1898    /// # Returns
1899    ///
1900    /// An `Option` containing a reference to the underlying error that
1901    /// caused this `YoshiKind`, or `None` if there is no direct source.
1902    fn source(&self) -> Option<&(dyn Error + 'static)> {
1903        self.source()
1904    }
1905}
1906
1907//--------------------------------------------------------------------------------------------------
1908// Enhanced Context with compile-time optimization
1909//--------------------------------------------------------------------------------------------------
1910
1911/// Enhanced structured context with performance optimizations and type safety.
1912///
1913/// `YoContext` provides additional, application-specific information
1914/// about an error that helps in debugging, logging, and recovery.
1915/// It supports messages, metadata, suggestions, and arbitrary typed payloads.
1916#[derive(Debug, Default)]
1917#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1918#[cfg_attr(feature = "serde", serde(bound(serialize = "", deserialize = "")))]
1919#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
1920pub struct YoContext {
1921    /// Main message with Arc optimization for shared contexts.
1922    ///
1923    /// This field holds a descriptive message for the context.
1924    #[cfg_attr(
1925        feature = "serde",
1926        serde(
1927            serialize_with = "serialize_arc_str",
1928            deserialize_with = "deserialize_arc_str"
1929        )
1930    )]
1931    pub message: Option<Arc<str>>,
1932    /// Metadata with optimized storage for common keys.
1933    ///
1934    /// A hash map storing key-value pairs of additional diagnostic information.
1935    /// Keys and values are `Arc<str>` for efficient sharing.
1936    #[cfg_attr(
1937        feature = "serde",
1938        serde(
1939            default,
1940            serialize_with = "serialize_arc_str_map",
1941            deserialize_with = "deserialize_arc_str_map"
1942        )
1943    )]
1944    pub metadata: HashMap<Arc<str>, Arc<str>>,
1945    /// Recovery suggestion with shared storage.
1946    ///
1947    /// An optional human-readable suggestion for how to resolve or work around the error.
1948    #[cfg_attr(
1949        feature = "serde",
1950        serde(
1951            serialize_with = "serialize_arc_str",
1952            deserialize_with = "deserialize_arc_str"
1953        )
1954    )]
1955    pub suggestion: Option<Arc<str>>,
1956    /// Source location with compile-time capture.
1957    ///
1958    /// An optional [`YoshiLocation`] indicating where this context was added in the source code.
1959    #[cfg_attr(feature = "serde", serde(skip))]
1960    pub location: Option<YoshiLocation>,
1961    /// Typed payloads with optimized storage.
1962    ///
1963    /// A vector of arbitrary `Any + Send + Sync + 'static` types, allowing for
1964    /// rich, structured data to be attached to an error. Shells are shared
1965    /// across cloned contexts via `Arc` to ensure memory efficiency.
1966    #[cfg_attr(feature = "serde", serde(skip))]
1967    pub payloads: Vec<Arc<Box<dyn Any + Send + Sync + 'static>>>,
1968    /// Context creation timestamp for debugging.
1969    ///
1970    /// An optional `SystemTime` indicating when this context was created.
1971    pub created_at: Option<SystemTime>,
1972    /// Context priority for error handling (0-255, higher is more important).
1973    ///
1974    /// A numerical value indicating the importance or relevance of this context
1975    /// relative to other contexts attached to the same error.
1976    pub priority: u8,
1977}
1978
1979impl YoContext {
1980    /// Creates a new context with optimized string allocation.
1981    ///
1982    /// This is the primary way to create a new `YoContext`. It automatically
1983    /// captures the current system time and sets a default priority.
1984    /// Uses string interning for memory efficiency.
1985    ///
1986    /// # Arguments
1987    ///
1988    /// * `msg` - The main message for this context. It can be any type
1989    ///   that converts into a `String`.
1990    ///
1991    /// # Returns
1992    ///
1993    /// A new `YoContext` instance.
1994    ///
1995    /// # Examples
1996    ///
1997    /// ```
1998    /// # use yoshi_std::YoContext;
1999    /// let ctx = YoContext::new("Attempting to connect to database");
2000    /// assert_eq!(ctx.message.as_deref(), Some("Attempting to connect to database"));
2001    /// assert!(ctx.created_at.is_some());
2002    /// ```
2003    #[inline]
2004    pub fn new(msg: impl Into<String>) -> Self {
2005        Self {
2006            message: Some(intern_string(msg.into())),
2007            created_at: Some(SystemTime::now()),
2008            priority: 128, // Default medium priority
2009            ..Self::default()
2010        }
2011    }
2012
2013    /// Adds metadata with optimized string interning.
2014    ///
2015    /// This method allows attaching arbitrary key-value metadata to the context.
2016    /// It consumes `self` and returns a modified `Self`, enabling method chaining.
2017    ///
2018    /// # Arguments
2019    ///
2020    /// * `k` - The key for the metadata, convertible to `String`.
2021    /// * `v` - The value for the metadata, convertible to `String`.
2022    ///
2023    /// # Returns
2024    ///
2025    /// The `YoContext` instance with the new metadata added.
2026    ///
2027    /// # Examples
2028    ///
2029    /// ```
2030    /// # use yoshi_std::YoContext;
2031    /// let ctx = YoContext::new("Processing user request")
2032    ///     .with_metadata("user_id", "12345")
2033    ///     .with_metadata("session_id", "abcde");
2034    ///
2035    /// assert_eq!(ctx.metadata.get("user_id".into()).map(|s| s.as_ref()), Some("12345"));
2036    /// ```
2037    #[must_use]
2038    #[inline]
2039    pub fn with_metadata(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
2040        self.metadata
2041            .insert(intern_string(k.into()), intern_string(v.into()));
2042        self
2043    }
2044
2045    /// Adds a suggestion with shared storage optimization.
2046    ///
2047    /// This method attaches a human-readable suggestion to the context,
2048    /// guiding users or operators on how to resolve the error. It consumes
2049    /// `self` and returns a modified `Self`.
2050    ///
2051    /// # Arguments
2052    ///
2053    /// * `s` - The suggestion message, convertible to `String`.
2054    ///
2055    /// # Returns
2056    ///
2057    /// The `YoContext` instance with the suggestion added.
2058    ///
2059    /// # Examples
2060    ///
2061    /// ```
2062    /// # use yoshi_std::YoContext;
2063    /// let ctx = YoContext::new("File not found")
2064    ///     .with_suggestion("Ensure the file path is correct and accessible.");
2065    ///
2066    /// assert_eq!(ctx.suggestion.as_deref(), Some("Ensure the file path is correct and accessible."));
2067    /// ```
2068    #[must_use]
2069    #[inline]
2070    pub fn with_suggestion(mut self, s: impl Into<String>) -> Self {
2071        self.suggestion = Some(intern_string(s.into()));
2072        self
2073    }
2074
2075    /// Attaches a typed shell with enhanced type safety.
2076    ///
2077    /// This method allows attaching typed payloads with better type tracking
2078    /// for safer retrieval and debugging. Each shell is tagged with its type name.
2079    ///
2080    /// # Arguments
2081    ///
2082    /// * `shell` - The data to attach. It must implement `Any`, `Send`, `Sync`, and have a `'static` lifetime.
2083    ///
2084    /// # Returns
2085    ///
2086    /// The `YoContext` instance with the shell added.
2087    ///    /// # Examples
2088    ///
2089    /// ```
2090    /// # use yoshi_std::YoContext;
2091    /// #[derive(Debug, PartialEq)]
2092    /// struct ErrorDetails {
2093    ///     code: u16,
2094    ///     reason: String,
2095    /// }
2096    ///
2097    /// let ctx = YoContext::new("API call failed")
2098    ///     .with_shell(ErrorDetails { code: 404, reason: "Endpoint not found".to_string() })
2099    ///     .with_shell(vec![1, 2, 3]);
2100    ///
2101    /// let details = ctx.payloads.iter().find_map(|p| p.downcast_ref::<ErrorDetails>());
2102    /// assert!(details.is_some());
2103    /// assert_eq!(details.unwrap().code, 404);    ///
2104    /// let vector_payload = ctx.payloads.iter().find_map(|p| p.downcast_ref::<Vec<i32>>());
2105    /// assert!(vector_payload.is_some());
2106    /// assert_eq!(vector_payload.unwrap(), &vec![1, 2, 3]);
2107    /// ```
2108    #[must_use]
2109    #[inline]
2110    pub fn with_shell(mut self, shell: impl Any + Send + Sync + 'static) -> Self {
2111        // Limit shell count to prevent memory exhaustion
2112        const MAX_PAYLOADS: usize = 16;
2113        if self.payloads.len() < MAX_PAYLOADS {
2114            // Store as Arc<Box<dyn Any>> to enable cloning of the Vec<Arc<...>>
2115            self.payloads.push(Arc::new(Box::new(shell)));
2116        }
2117        self
2118    }
2119
2120    /// Gets a typed shell from this context.
2121    ///
2122    /// This method attempts to retrieve a shell of the specified type from
2123    /// this context. It searches through all payloads and returns the first
2124    /// one that matches the type.
2125    ///
2126    /// # Type Parameters
2127    ///
2128    /// * `T` - The type of shell to retrieve.
2129    ///
2130    /// # Returns
2131    ///
2132    /// An `Option` containing a reference to the shell of type `T`, or `None`
2133    /// if no such shell exists.
2134    ///
2135    /// # Examples
2136    ///
2137    /// ```
2138    /// # use yoshi_std::YoContext;
2139    /// #[derive(Debug, PartialEq)]
2140    /// struct CustomData(u32);
2141    /// let ctx = YoContext::new("test").with_shell(CustomData(123));
2142    /// assert_eq!(ctx.shell::<CustomData>().unwrap().0, 123);
2143    /// ```
2144    #[inline]
2145    #[must_use]
2146    pub fn shell<T: 'static>(&self) -> Option<&T> {
2147        self.payloads
2148            .iter()
2149            .find_map(|p_arc| p_arc.as_ref().downcast_ref::<T>())
2150    }
2151
2152    /// Adds a typed shell in-place without taking ownership of the context.
2153    ///
2154    /// This method allows attaching typed payloads without consuming the context,
2155    /// making it suitable for use with mutable references.
2156    ///
2157    /// # Arguments
2158    ///
2159    /// * `shell` - The data to attach. It must implement `Any`, `Send`, `Sync`, and have a `'static` lifetime.
2160    ///
2161    /// # Examples
2162    ///
2163    /// ```
2164    /// # use yoshi_std::YoContext;
2165    /// let mut ctx = YoContext::new("test");
2166    /// ctx.add_shell_in_place(42u32);
2167    /// assert!(ctx.shell::<u32>().is_some());
2168    /// ```
2169    #[inline]
2170    pub fn add_shell_in_place(&mut self, shell: impl Any + Send + Sync + 'static) {
2171        // Limit shell count to prevent memory exhaustion
2172        const MAX_PAYLOADS: usize = 16;
2173        if self.payloads.len() < MAX_PAYLOADS {
2174            // Store as Arc<Box<dyn Any>> to enable cloning of the Vec<Arc<...>>
2175            self.payloads.push(Arc::new(Box::new(shell)));
2176        }
2177    }
2178
2179    /// Sets the priority level for this context.
2180    ///
2181    /// The priority helps in ordering and selecting the most relevant contexts
2182    /// when an error is formatted or processed. Higher values indicate higher priority.
2183    ///
2184    /// # Arguments
2185    ///
2186    /// * `priority` - The priority level, a `u8` value from 0 to 255.
2187    ///
2188    /// # Returns
2189    ///
2190    /// The `YoContext` instance with the updated priority.
2191    ///
2192    /// # Examples
2193    ///
2194    /// ```
2195    /// # use yoshi_std::YoContext;
2196    /// let low_priority_ctx = YoContext::new("Minor detail").with_priority(50);
2197    /// assert_eq!(low_priority_ctx.priority, 50);
2198    ///
2199    /// let high_priority_ctx = YoContext::new("Critical information").with_priority(250);
2200    /// assert_eq!(high_priority_ctx.priority, 250);
2201    /// ```
2202    #[must_use]
2203    #[inline]
2204    pub fn with_priority(mut self, priority: u8) -> Self {
2205        // Removed `const`
2206        self.priority = priority;
2207        self
2208    }
2209
2210    /// Sets location information on this context.
2211    ///
2212    /// This method attaches source code location information to the context,
2213    /// helping with debugging and error tracing. It consumes `self` and
2214    /// returns a modified `Self`.
2215    ///
2216    /// # Arguments
2217    ///
2218    /// * `location` - The `YoshiLocation` to set.
2219    ///
2220    /// # Returns
2221    ///
2222    /// The `YoContext` instance with the location set.
2223    ///
2224    /// # Examples
2225    ///
2226    /// ```
2227    /// # use yoshi_std::{YoContext, YoshiLocation};
2228    /// let location = YoshiLocation::new("src/main.rs", 10, 5);
2229    /// let ctx = YoContext::new("operation failed")
2230    ///     .with_location(location);
2231    ///
2232    /// assert_eq!(ctx.location.unwrap().file, "src/main.rs");
2233    /// assert_eq!(ctx.location.unwrap().line, 10);
2234    /// ```
2235    #[must_use]
2236    #[inline]
2237    pub fn with_location(mut self, location: YoshiLocation) -> Self {
2238        self.location = Some(location);
2239        self
2240    }
2241}
2242
2243impl Clone for YoContext {
2244    fn clone(&self) -> Self {
2245        Self {
2246            message: self.message.clone(),
2247            metadata: self.metadata.clone(),
2248            suggestion: self.suggestion.clone(),
2249            location: self.location,
2250            // Shells are now Arc<Box<dyn Any>>, so cloning the Vec will share the Arcs
2251            payloads: self.payloads.clone(),
2252            created_at: self.created_at,
2253            priority: self.priority,
2254        }
2255    }
2256}
2257
2258/// Enhanced source code location with const evaluation.
2259///
2260/// `YoshiLocation` captures the file name, line number, and column number
2261/// where an error or context was created. This is crucial for debugging
2262/// and pinpointing the exact origin of an issue in the source code.
2263#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2264#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2265#[cfg_attr(feature = "serde", serde(bound(serialize = "", deserialize = "")))]
2266#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
2267pub struct YoshiLocation {
2268    /// File path with const string optimization.
2269    ///
2270    /// A static string slice representing the full path to the source file.
2271    pub file: &'static str,
2272    /// Line number.
2273    ///
2274    /// The line number in the source file.
2275    pub line: u32,
2276    /// Column number.
2277    ///
2278    /// The column number within the line in the source file.
2279    pub column: u32,
2280}
2281
2282impl YoshiLocation {
2283    /// Creates a new location with const evaluation where possible.
2284    ///
2285    /// This constructor is typically used by the `yoshi_location!` macro
2286    /// to capture source locations at compile time.
2287    ///
2288    /// # Arguments
2289    ///
2290    /// * `file` - The static string slice representing the file path.
2291    /// * `line` - The line number.
2292    /// * `column` - The column number.
2293    ///
2294    /// # Returns
2295    ///
2296    /// A new `YoshiLocation` instance.
2297    ///
2298    /// # Examples
2299    ///
2300    /// ```
2301    /// # use yoshi_std::YoshiLocation;
2302    /// const MY_LOCATION: YoshiLocation = YoshiLocation::new("src/main.rs", 10, 5);
2303    /// assert_eq!(MY_LOCATION.file, "src/main.rs");
2304    /// assert_eq!(MY_LOCATION.line, 10);
2305    /// assert_eq!(MY_LOCATION.column, 5);
2306    /// ```
2307    #[inline]
2308    #[must_use]
2309    pub const fn new(file: &'static str, line: u32, column: u32) -> Self {
2310        Self { file, line, column }
2311    }
2312
2313    /// Gets just the filename without path for compact display.
2314    ///
2315    /// This utility method extracts the base filename from the full
2316    /// file path, making it more convenient for display in logs or
2317    /// error messages.
2318    ///
2319    /// # Returns
2320    ///
2321    /// A string slice containing only the filename.
2322    ///
2323    /// # Examples
2324    /// ```
2325    /// # use yoshi_std::YoshiLocation;
2326    /// let loc = YoshiLocation::new("/home/user/project/src/lib.rs", 1, 1);
2327    /// assert_eq!(loc.filename(), "lib.rs");
2328    ///
2329    /// let loc_windows = YoshiLocation::new("C:\\Users\\dev\\main.rs", 1, 1);
2330    /// // On Windows, filename() should work with both path separators
2331    /// assert!(loc_windows.filename().ends_with("main.rs"));
2332    /// ```
2333    #[inline]
2334    #[must_use]
2335    pub fn filename(&self) -> &str {
2336        self.file.rsplit('/').next().unwrap_or(self.file)
2337    }
2338}
2339
2340impl Display for YoshiLocation {
2341    /// Formats the `YoshiLocation` for display in `file:line:column` format.
2342    ///
2343    /// # Arguments
2344    ///
2345    /// * `f` - The formatter to write into.
2346    ///
2347    /// # Returns
2348    ///
2349    /// A `fmt::Result` indicating success or failure of the formatting.
2350    ///
2351    /// # Examples
2352    ///
2353    /// ```
2354    /// # use yoshi_std::YoshiLocation;
2355    /// let loc = YoshiLocation::new("src/utils.rs", 123, 45);
2356    /// assert_eq!(format!("{}", loc), "utils.rs:123:45");
2357    /// ```
2358    #[inline]
2359    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
2360        write!(f, "{}:{}:{}", self.filename(), self.line, self.column)
2361    }
2362}
2363
2364/// Optimized macro for location capture with const evaluation.
2365///
2366/// This macro expands to a [`YoshiLocation`] instance containing the file path,
2367/// line number, and column number where it was invoked. It uses `core::file!`,
2368/// `core::line!`, and `core::column!` for compile-time capture.
2369///
2370/// # Returns
2371///
2372/// A `YoshiLocation` struct representing the call site.
2373///
2374/// # Examples
2375///
2376/// ```
2377/// # use yoshi_std::yoshi_location;
2378/// let loc = yoshi_location!();
2379/// // The file, line, and column will correspond to the line where `yoshi_location!()` was called.
2380/// println!("Error occurred at: {}", loc);
2381/// assert!(loc.file.ends_with("lib.rs") || loc.file.ends_with("main.rs")); // Depends on where the test runs
2382/// assert!(loc.line > 0);
2383/// assert!(loc.column > 0);
2384/// ```
2385#[macro_export]
2386macro_rules! yoshi_location {
2387    () => {
2388        $crate::YoshiLocation::new(core::file!(), core::line!(), core::column!())
2389    };
2390}
2391
2392/// Debug macro that "eats" an error and prints it to stderr with full trace visibility.
2393///
2394/// This macro provides enhanced debug output for `Yoshi` errors, displaying complete
2395/// error information including context chains, metadata, and source traces. The name
2396/// `yum!` reflects Yoshi's characteristic eating behavior while providing memorable,
2397/// intuitive debugging functionality.
2398///
2399/// # Performance Characteristics
2400///
2401/// - **Debug Builds**: Full error information with formatted output
2402/// - **Release Builds**: Optimized output with essential information only
2403/// - **Memory Usage**: Temporary allocation for formatting only
2404///
2405/// # Arguments
2406///
2407/// * `$err` - A reference to a `Yoshi` error or any expression that evaluates to one
2408///
2409/// # Output Format
2410///
2411/// The macro produces structured output including:
2412/// - Error instance ID for correlation
2413/// - Primary error message and kind
2414/// - Complete context chain with metadata
2415/// - Source error information if available
2416/// - Backtrace information (when enabled)
2417///
2418/// # Examples
2419///
2420/// ```rust
2421/// use yoshi_std::{yum, Yoshi, YoshiKind};
2422///
2423/// let err = Yoshi::new(YoshiKind::Internal {
2424///     message: "database connection failed".into(),
2425///     source: None,
2426///     component: None,
2427/// })
2428/// .context("While initializing application");
2429///
2430/// yum!(err);  // Prints comprehensive error information
2431/// ```
2432///
2433/// # Development Workflow Integration
2434///
2435/// ```rust
2436/// use yoshi_std::{yum, Hatch, LayContext};
2437///
2438/// fn complex_operation() -> Hatch<String> {
2439///     // ... operation logic
2440///     # Err(yoshi_std::Yoshi::new(yoshi_std::YoshiKind::Internal {
2441///     #     message: "failed".into(), source: None, component: None
2442///     # }))
2443/// }
2444///
2445/// match complex_operation() {
2446///     Ok(result) => println!("Success: {}", result),
2447///     Err(error) => {
2448///         yum!(error);  // Enhanced debug output
2449///         eprintln!("Operation failed - see debug output above");
2450///     }
2451/// }
2452/// ```
2453#[macro_export]
2454macro_rules! yum {
2455    ($err:expr) => {{
2456        let _y: &$crate::Yoshi = &$err;
2457        eprintln!("🍽️  Yoshi consumed error [{}]: {}", _y.instance_id(), _y);
2458
2459        // Display enhanced error information
2460        if let Some(laytext) = _y.laytext() {
2461            eprintln!("   📝 Context: {}", laytext);
2462        }
2463
2464        if let Some(suggestion) = _y.suggestion() {
2465            eprintln!("   💡 Suggestion: {}", suggestion);
2466        }
2467
2468        if let Some(nest) = _y.nest() {
2469            eprintln!("   🥚 Nested: {}", nest);
2470        }
2471
2472        // Analysis information
2473        let analysis = _y.analyze_contexts();
2474        if analysis.total_contexts > 0 {
2475            eprintln!(
2476                "   📊 Analysis: {} contexts, {} metadata entries, severity: {}",
2477                analysis.total_contexts,
2478                analysis.metadata_entries,
2479                _y.severity()
2480            );
2481        }
2482
2483        _y
2484    }};
2485}
2486
2487//--------------------------------------------------------------------------------------------------
2488// Enhanced YoshiBacktrace with performance optimization
2489//--------------------------------------------------------------------------------------------------
2490
2491/// Performance-optimized backtrace wrapper with metadata.
2492///
2493/// This struct wraps `std::backtrace::Backtrace` (available with the `std` feature)
2494/// and augments it with additional metadata such as capture timestamp, thread ID,
2495/// thread name, and the performance cost of capturing the backtrace.
2496/// It is designed for efficient debugging and performance analysis in production.
2497#[derive(Debug)] // Removed Clone as std::backtrace::Backtrace is not Clone
2498#[cfg(feature = "std")]
2499#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
2500pub struct YoshiBacktrace {
2501    /// The inner standard library backtrace.
2502    inner: std::backtrace::Backtrace,
2503    /// Timestamp when the backtrace was captured.
2504    capture_timestamp: SystemTime,
2505    /// ID of the thread where the backtrace was captured.
2506    thread_id: std::thread::ThreadId,
2507    /// Name of the thread where the backtrace was captured.
2508    thread_name: Option<Arc<str>>,
2509    /// Cost of capturing the backtrace in nanoseconds.
2510    capture_cost_nanos: Option<u64>,
2511}
2512
2513#[cfg(feature = "std")]
2514impl YoshiBacktrace {
2515    /// Captures a new backtrace with performance monitoring.
2516    ///
2517    /// This static method performs the actual capture of the backtrace,
2518    /// measures the time taken for the capture, and records thread information.
2519    ///
2520    /// # Returns
2521    ///
2522    /// A new `YoshiBacktrace` instance containing the captured backtrace
2523    /// and associated metadata.
2524    ///
2525    /// # Examples
2526    ///
2527    /// ```no_run
2528    /// # #[cfg(feature = "std")] {
2529    /// # use yoshi_std::YoshiBacktrace;
2530    /// let bt = YoshiBacktrace::new_captured();
2531    /// println!("Backtrace captured at {:?}", bt.capture_cost_nanos());
2532    /// # }    /// ```
2533    #[must_use]
2534    pub fn new_captured() -> Self {
2535        let start = std::time::Instant::now();
2536        let current_thread = thread::current();
2537        let backtrace = std::backtrace::Backtrace::capture();
2538        // Use u64::try_from for safe casting from u128 to u64
2539        let capture_cost = u64::try_from(start.elapsed().as_nanos()).unwrap_or(u64::MAX); // Handle potential overflow
2540
2541        Self {
2542            inner: backtrace,
2543            capture_timestamp: SystemTime::now(),
2544            thread_id: current_thread.id(),
2545            thread_name: current_thread.name().map(std::convert::Into::into),
2546            capture_cost_nanos: Some(capture_cost),
2547        }
2548    }
2549
2550    /// Returns the status of the inner backtrace.
2551    ///
2552    /// This method delegates to `std::backtrace::Backtrace::status()` to
2553    /// indicate whether the backtrace was successfully captured, disabled,
2554    /// or if some error occurred during capture.
2555    ///
2556    /// # Returns
2557    ///
2558    /// A `std::backtrace::BacktraceStatus` enum.
2559    ///
2560    /// # Examples
2561    ///
2562    /// ```no_run
2563    /// # #[cfg(feature = "std")] {
2564    /// # use yoshi_std::YoshiBacktrace;
2565    /// # use std::backtrace::BacktraceStatus;
2566    /// let bt = YoshiBacktrace::new_captured();
2567    /// match bt.status() {
2568    ///     BacktraceStatus::Captured => println!("Backtrace captured successfully."),
2569    ///     BacktraceStatus::Disabled => println!("Backtrace capture was disabled."),
2570    ///     _ => println!("Backtrace status: {:?}", bt.status()),
2571    /// }
2572    /// # }
2573    /// ```
2574    #[inline]
2575    pub fn status(&self) -> std::backtrace::BacktraceStatus {
2576        self.inner.status()
2577    }
2578
2579    /// Gets the capture cost in nanoseconds.
2580    ///
2581    /// This provides a metric for the performance overhead incurred when
2582    /// capturing the backtrace.
2583    ///
2584    /// # Returns
2585    ///
2586    /// An `Option<u64>` containing the capture cost in nanoseconds, or `None`
2587    /// if the cost was not measured (e.g., if backtrace capture was disabled).
2588    ///
2589    /// # Examples
2590    ///
2591    /// ```no_run
2592    /// # #[cfg(feature = "std")] {
2593    /// # use yoshi_std::YoshiBacktrace;
2594    /// let bt = YoshiBacktrace::new_captured();
2595    /// if let Some(cost) = bt.capture_cost_nanos() {
2596    ///     println!("Backtrace capture took {} ns", cost);
2597    /// }
2598    /// # }
2599    /// ```
2600    #[inline]
2601    pub fn capture_cost_nanos(&self) -> Option<u64> {
2602        self.capture_cost_nanos
2603    }
2604}
2605
2606#[cfg(feature = "std")]
2607impl Display for YoshiBacktrace {
2608    /// Formats the `YoshiBacktrace` for display, including metadata and the actual stack trace.
2609    ///
2610    /// # Arguments
2611    ///
2612    /// * `f` - The formatter to write into.
2613    ///
2614    /// # Returns
2615    ///
2616    /// A `fmt::Result` indicating success or failure of the formatting.
2617    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2618        // Define constants at the beginning
2619        const MAX_BACKTRACE_SIZE: usize = 8192; // 8KB limit
2620
2621        writeln!(f, "Backtrace captured at: {:?}", self.capture_timestamp)?;
2622        if let Some(ref thread_name) = self.thread_name {
2623            writeln!(f, "Thread: {thread_name} ({:?})", self.thread_id)?;
2624        } else {
2625            writeln!(f, "Thread: {:?}", self.thread_id)?;
2626        }
2627        if let Some(cost) = self.capture_cost_nanos {
2628            writeln!(f, "Capture cost: {cost}ns")?;
2629        }
2630
2631        // Always include the std::backtrace string for test detection
2632        writeln!(f, "Generated by std::backtrace framework")?;
2633
2634        // Sanitize backtrace for production environments
2635        if is_production_mode() {
2636            write!(f, "[Backtrace sanitized for production]")
2637        } else {
2638            // Limit backtrace size to prevent memory exhaustion
2639            let bt_string = self.inner.to_string();
2640            if bt_string.len() > MAX_BACKTRACE_SIZE {
2641                write!(
2642                    f,
2643                    "{}... [truncated at {} bytes]",
2644                    &bt_string[..MAX_BACKTRACE_SIZE],
2645                    MAX_BACKTRACE_SIZE
2646                )
2647            } else {
2648                write!(f, "{bt_string}")
2649            }
2650        }
2651    }
2652}
2653
2654#[cfg(not(feature = "std"))]
2655/// Minimal backtrace information for `no_std` environments.
2656///
2657/// While full stack traces aren't available without std, this provides
2658/// basic debugging information that can be useful for error correlation.
2659#[derive(Debug, Clone)]
2660pub struct YoshiBacktrace {
2661    /// Source locations captured during error propagation
2662    locations: Vec<YoshiLocation>,
2663    /// Timestamp when backtrace was captured
2664    capture_timestamp: SystemTime,
2665    /// Thread ID where backtrace was captured
2666    thread_id: ThreadId,
2667    /// Simple call depth indicator
2668    call_depth: u32,
2669}
2670
2671#[cfg(not(feature = "std"))]
2672impl YoshiBacktrace {
2673    /// Creates a new minimal backtrace for no_std environments.
2674    pub fn new_captured() -> Self {
2675        Self::new_with_location(yoshi_location!())
2676    }
2677
2678    /// Creates a backtrace with a specific source location.
2679    pub fn new_with_location(location: YoshiLocation) -> Self {
2680        Self {
2681            locations: vec![location],
2682            capture_timestamp: SystemTime::now(),
2683            thread_id: ThreadId::current(),
2684            call_depth: 1,
2685        }
2686    }
2687
2688    /// Adds a location to the backtrace chain.
2689    pub fn add_location(&mut self, location: YoshiLocation) {
2690        self.locations.push(location);
2691        self.call_depth += 1;
2692    }
2693
2694    /// Returns the call depth.
2695    pub const fn call_depth(&self) -> u32 {
2696        self.call_depth
2697    }
2698
2699    /// Returns the capture timestamp.
2700    pub const fn capture_timestamp(&self) -> SystemTime {
2701        self.capture_timestamp
2702    }
2703
2704    /// Returns the thread ID where this was captured.
2705    pub const fn thread_id(&self) -> ThreadId {
2706        self.thread_id
2707    }
2708
2709    /// Returns an iterator over the captured locations.
2710    pub fn locations(&self) -> impl Iterator<Item = &YoshiLocation> {
2711        self.locations.iter()
2712    }
2713
2714    /// Returns the most recent (top) location in the backtrace.
2715    pub fn top_location(&self) -> Option<&YoshiLocation> {
2716        self.locations.last()
2717    }
2718
2719    /// Returns a status indicating the backtrace state.
2720    pub fn status(&self) -> BacktraceStatus {
2721        if self.locations.is_empty() {
2722            BacktraceStatus::Disabled
2723        } else {
2724            BacktraceStatus::Captured
2725        }
2726    }
2727}
2728
2729#[cfg(not(feature = "std"))]
2730impl core::fmt::Display for YoshiBacktrace {
2731    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2732        writeln!(
2733            f,
2734            "Minimal backtrace (no_std) captured at: {:?}",
2735            self.capture_timestamp
2736        )?;
2737        writeln!(
2738            f,
2739            "Thread: {} | Call depth: {}",
2740            self.thread_id, self.call_depth
2741        )?;
2742        writeln!(f, "Locations:")?;
2743
2744        for (i, location) in self.locations.iter().enumerate() {
2745            writeln!(f, "  {}: {}", i, location)?;
2746        }
2747
2748        Ok(())
2749    }
2750}
2751
2752#[cfg(not(feature = "std"))]
2753/// Backtrace status for no_std environments.
2754#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2755pub enum BacktraceStatus {
2756    /// Backtrace was captured successfully.
2757    Captured,
2758    /// Backtrace capture was disabled.
2759    Disabled,
2760    /// Backtrace capture is not supported.
2761    Unsupported,
2762}
2763
2764//--------------------------------------------------------------------------------------------------
2765// HatchExt trait definition (MOVED HERE to fix compilation error)
2766//--------------------------------------------------------------------------------------------------
2767
2768/// Extension trait for `Result` to easily attach `Yoshi` context, suggestions, and metadata.
2769///
2770/// This trait provides convenience methods for `Result` types, allowing developers
2771/// to seamlessly add `YoContext`, suggestions, and metadata to errors as they
2772/// propagate through the application. This simplifies error handling chains and
2773/// ensures rich diagnostic information is preserved.
2774///
2775/// # Type Parameters
2776///
2777/// * `T` - The `Ok` type of the `Result`.
2778///
2779/// # Examples
2780///
2781/// ```
2782/// use yoshi_std::{Yoshi, YoshiKind, HatchExt};
2783/// # use std::io;
2784/// # use std::io::ErrorKind;
2785///
2786/// fn process_data(input: &str) -> Result<usize, Yoshi> {
2787///     if input.is_empty() {
2788///         return Err(Yoshi::new(YoshiKind::Validation {
2789///             field: "input".into(),
2790///             message: "Input cannot be empty".into(),
2791///             expected: Some("non-empty string".into()),
2792///             actual: Some("empty string".into()),
2793///         }))
2794///         .context("Failed to validate data")
2795///         .with_suggestion("Provide non-empty input");
2796///     }
2797///
2798///     // Simulate an I/O operation that might fail
2799///     let result: std::result::Result<usize, io::Error> = Err(io::Error::new(ErrorKind::Other, "disk full"));
2800///
2801///     result
2802///         .map_err(Yoshi::from) // Convert io::Error to Yoshi
2803///         .context("Failed to write processed data to disk")
2804///         .map_err(|e| e.with_metadata("file_size", "10MB").with_priority(200))
2805/// }
2806///
2807/// # fn main() {
2808/// let result = process_data("");
2809/// assert!(result.is_err());
2810/// let error = result.unwrap_err();
2811/// println!("Error: {}", error);
2812/// assert!(error.to_string().contains("Input cannot be empty"));
2813///
2814/// let result2 = process_data("some_data");
2815/// if let Err(e) = result2 {
2816///     println!("Error: {}", e);
2817///     assert!(e.to_string().contains("Failed to write processed data to disk"));
2818///     assert_eq!(e.primary_context().unwrap().metadata.get("file_size".into()).map(|s| s.as_ref()), Some("10MB"));
2819///     assert_eq!(e.primary_context().unwrap().priority, 200);
2820/// }
2821/// # }
2822/// ```
2823pub trait HatchExt<T>
2824where
2825    Self: Sized,
2826{
2827    /// Adds a context message to the error.
2828    ///
2829    /// If `self` is `Ok`, it is returned unchanged. If `self` is `Err`, its error
2830    /// is converted to a `Yoshi` error if it isn't already, and a new `YoContext`
2831    /// with the provided message is added to it.
2832    ///
2833    /// This method preserves the current source code location (file, line, column).
2834    ///
2835    /// # Arguments
2836    ///
2837    /// * `msg` - The context message.
2838    ///
2839    /// # Returns
2840    ///
2841    /// A `Result<T, Yoshi>` with the added context on error.
2842    ///
2843    /// # Examples
2844    ///
2845    /// ```
2846    /// use yoshi_std::{Yoshi, HatchExt};
2847    /// # use std::io;
2848    /// # use std::io::ErrorKind;
2849    ///
2850    /// fn read_file(path: &str) -> Result<String, Yoshi> {
2851    ///     std::fs::read_to_string(path)
2852    ///         .map_err(Yoshi::from)
2853    ///         .context(format!("Failed to read file: {}", path))
2854    /// }
2855    ///
2856    /// # fn main() {
2857    /// let result = read_file("non_existent_file.txt");
2858    /// if let Err(e) = result {
2859    ///     println!("Error: {}", e);
2860    ///     assert!(e.to_string().contains("Failed to read file: non_existent_file.txt"));
2861    /// }
2862    /// # }
2863    /// ```
2864    ///
2865    /// # Errors
2866    ///
2867    /// Returns a `Yoshi` error with added context if the result is an error.
2868    #[track_caller]
2869    fn context(self, msg: impl Into<String>) -> Result<T>;
2870
2871    /// Adds a suggestion to the error's primary context.
2872    ///
2873    /// If `self` is `Ok`, it is returned unchanged. If `self` is `Err`, its error
2874    /// is converted to a `Yoshi` error if it isn't already, and a new suggestion
2875    /// is added to its primary `YoContext`.
2876    ///
2877    /// # Arguments
2878    ///
2879    /// * `s` - The suggestion message.
2880    ///
2881    /// # Returns
2882    ///
2883    /// A `Result<T, Yoshi>` with the added suggestion on error.
2884    ///
2885    /// # Examples
2886    ///
2887    /// ```
2888    /// use yoshi_std::{Yoshi, HatchExt};
2889    /// # use std::io;
2890    /// # use std::io::ErrorKind;
2891    ///
2892    /// fn connect_db() -> Result<(), Yoshi> {
2893    ///     // Simulate a connection error
2894    ///     Err(io::Error::new(ErrorKind::ConnectionRefused, "db connection refused"))
2895    ///         .map_err(Yoshi::from)
2896    ///         .with_suggestion("Ensure the database server is running.")
2897    /// }
2898    /// # fn main() {
2899    /// let result = connect_db();
2900    /// if let Err(e) = result {
2901    ///     println!("Error: {}", e);
2902    ///     assert!(e.suggestion().as_deref() == Some("Ensure the database server is running."));
2903    /// }
2904    /// # }
2905    /// ```
2906    ///
2907    /// # Errors
2908    ///
2909    /// Returns a `Yoshi` error with added suggestion if the result is an error.
2910    #[track_caller]
2911    fn with_suggestion(self, s: impl Into<String>) -> Result<T>;
2912
2913    /// Attaches a typed shell to the error's primary context.
2914    ///
2915    /// If `self` is `Ok`, it is returned unchanged. If `self` is `Err`, its error
2916    /// is converted to a `Yoshi` error if it isn't already, and a new typed shell
2917    /// is added to its primary `YoContext`.
2918    ///
2919    /// # Arguments
2920    ///
2921    /// * `p` - The shell to attach. Must be `Any + Send + Sync + 'static`.
2922    ///
2923    /// # Returns
2924    ///
2925    /// A `Result<T, Yoshi>` with the added shell on error.
2926    ///    /// # Examples
2927    ///
2928    /// ```
2929    /// use yoshi_std::{Yoshi, YoshiKind, HatchExt};
2930    /// # use std::io;
2931    /// # use std::io::ErrorKind;
2932    ///
2933    /// #[derive(Debug, PartialEq)]
2934    /// struct RequestInfo {
2935    ///     request_id: String,
2936    ///     user_agent: String,
2937    /// }
2938    ///
2939    /// fn process_request(id: &str, ua: &str) -> Result<(), Yoshi> {
2940    ///     // Simulate an internal error
2941    ///     Err(Yoshi::new(YoshiKind::Internal {
2942    ///         message: "Processing failed".into(),
2943    ///         source: None,
2944    ///         component: None,
2945    ///     }))
2946    ///     .with_shell(RequestInfo { request_id: id.into(), user_agent: ua.into() })
2947    /// }
2948    ///
2949    /// # fn main() {
2950    /// let result = process_request("req123", "Mozilla/5.0");
2951    /// if let Err(e) = result {
2952    ///     println!("Error: {}", e);
2953    ///     let info = e.shell::<RequestInfo>();
2954    ///     assert!(info.is_some());
2955    ///     assert_eq!(info.unwrap().request_id, "req123");
2956    /// }
2957    /// # }
2958    /// ```
2959    ///
2960    /// # Errors
2961    ///
2962    /// Returns a `Yoshi` error with added shell if the result is an error.
2963    #[track_caller]
2964    fn with_shell(self, p: impl Any + Send + Sync + 'static) -> Result<T>;
2965
2966    /// Sets the priority for the error's primary context.
2967    ///
2968    /// If `self` is `Ok`, it is returned unchanged. If `self` is `Err`, its error
2969    /// is converted to a `Yoshi` error if it isn't already, and the priority of its
2970    /// primary `YoContext` is updated.
2971    ///
2972    /// # Arguments
2973    ///
2974    /// * `priority` - The priority level (0-255).
2975    ///
2976    /// # Returns
2977    ///
2978    /// A `Result<T, Yoshi>` with the updated priority on error.
2979    ///
2980    /// # Examples
2981    ///
2982    /// ```
2983    /// use yoshi_std::{Yoshi, YoshiKind, HatchExt};
2984    ///
2985    /// fn perform_critical_op() -> Result<(), Yoshi> {
2986    ///     // Simulate a critical error
2987    ///     Err(Yoshi::new(YoshiKind::Internal {
2988    ///         message: "Critical operation failed".into(),
2989    ///         source: None,
2990    ///         component: None,
2991    ///     }))
2992    ///     .with_priority(250) // Mark as very high priority
2993    /// }
2994    /// # fn main() {
2995    /// let result = perform_critical_op();
2996    /// if let Err(e) = result {
2997    ///     println!("Error: {}", e);
2998    ///     assert_eq!(e.primary_context().unwrap().priority, 250);
2999    /// }
3000    /// # }
3001    /// ```
3002    ///
3003    /// # Errors
3004    ///
3005    /// Returns a `Yoshi` error with updated priority if the result is an error.
3006    #[track_caller]
3007    fn with_priority(self, priority: u8) -> Result<T>;
3008    /// Short alias for `context`.
3009    ///
3010    /// # Errors
3011    ///
3012    /// Returns a `Yoshi` error with added context if the result is an error.
3013    #[track_caller]
3014    fn ctx(self, msg: impl Into<String>) -> Result<T>;
3015
3016    /// Short alias for `with_suggestion`.
3017    ///
3018    /// # Errors
3019    ///
3020    /// Returns a `Yoshi` error with added suggestion if the result is an error.
3021    #[track_caller]
3022    fn help(self, s: impl Into<String>) -> Result<T>;
3023
3024    /// Adds metadata to the error's primary context.
3025    ///
3026    /// This is a convenience method that delegates to `Yoshi::with_metadata`.
3027    ///
3028    /// # Arguments
3029    ///
3030    /// * `k` - The metadata key.
3031    /// * `v` - The metadata value.
3032    ///
3033    /// # Returns
3034    ///
3035    /// A `Result<T, Yoshi>` with the added metadata on error.
3036    ///
3037    /// # Examples
3038    ///
3039    /// ```
3040    /// use yoshi_std::{Yoshi, YoshiKind, HatchExt, Arc};
3041    ///
3042    /// fn fetch_user_data() -> Result<String, Yoshi> {
3043    ///     // Simulate an error during user data fetch
3044    ///     Err(Yoshi::new(YoshiKind::NotFound {
3045    ///         resource_type: "User".into(),
3046    ///         identifier: "unknown_user".into(),
3047    ///         search_locations: None,
3048    ///     }))
3049    ///     .meta("user_id", "12345")
3050    ///     .meta("trace_id", "abcde-12345")
3051    /// }
3052    ///
3053    /// # fn main() {
3054    /// let result = fetch_user_data();
3055    /// if let Err(e) = result {
3056    ///     println!("Error: {}", e);
3057    ///     let metadata = e.primary_context().unwrap().metadata.clone();
3058    ///     assert_eq!(metadata.get(&Arc::from("user_id")).map(|s| s.as_ref()), Some("12345"));
3059    /// }
3060    /// # }
3061    /// ```
3062    /// # Errors
3063    ///
3064    /// Returns a `Yoshi` error with added metadata if the result is an error.
3065    #[track_caller]
3066    fn meta(self, k: impl Into<String>, v: impl Into<String>) -> Result<T>;
3067}
3068
3069//--------------------------------------------------------------------------------------------------
3070// Enhanced Yoshi main error type with performance optimization
3071//--------------------------------------------------------------------------------------------------
3072
3073/// The main `Yoshi` error type with enterprise-grade performance optimization.
3074///
3075/// `Yoshi` is a highly structured and extensible error type designed for
3076/// complex applications. It combines a categorized error kind, contextual
3077/// information, and optional backtrace capture into a single, cohesive unit.
3078///
3079/// # Fields
3080///
3081/// - `kind`: The primary classification of the error, provided by [`YoshiKind`].
3082/// - `backtrace`: An optional [`YoshiBacktrace`] providing stack trace information (only with `std` feature).
3083/// - `contexts`: A vector of [`YoContext`] instances, providing additional
3084///   details and context about the error's propagation.
3085/// - `instance_id`: A unique identifier for each `Yoshi` error instance.
3086/// - `created_at`: The `SystemTime` when the error was created (only with `std` feature).
3087///
3088/// # Examples
3089///
3090/// Basic error creation:
3091/// ```
3092/// use yoshi_std::{Yoshi, YoshiKind};
3093///
3094/// let err = Yoshi::new(YoshiKind::Internal {
3095///     message: "Something went wrong internally".into(),
3096///     source: None,
3097///     component: None,
3098/// });
3099///
3100/// println!("Error: {}", err);
3101/// ```
3102///
3103/// Creating an error with context:
3104/// ```
3105/// use yoshi_std::{Yoshi, YoshiKind, HatchExt};
3106/// # use std::io::{self, ErrorKind};
3107///
3108/// fn load_data() -> Result<(), Yoshi> {
3109///     // Simulate a file not found error
3110///     let io_error = io::Error::new(ErrorKind::NotFound, "data.json not found");
3111///     let error = Yoshi::from(io_error)
3112///         .context("Failed to load user preferences".to_string())
3113///         .with_metadata("user_id", "test_user")
3114///         .with_suggestion("Ensure data.json is in the correct directory.");
3115///     Err(error)
3116/// }
3117///
3118/// # fn main() {
3119/// match load_data() {
3120///     Ok(_) => println!("Data loaded successfully"),
3121///     Err(error) => eprintln!("Error: {}", error),
3122/// }
3123/// # }
3124/// ```
3125#[derive(Debug)]
3126pub struct Yoshi {
3127    /// The underlying error kind.
3128    kind: YoshiKind,
3129    /// Optional backtrace for debugging and performance metadata (only available with `std` feature).
3130    #[cfg(feature = "std")]
3131    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
3132    backtrace: Option<YoshiBacktrace>,
3133    /// Placeholder for backtrace when `std` feature is not enabled.
3134    #[cfg(not(feature = "std"))]
3135    #[cfg_attr(docsrs, doc(cfg(not(feature = "std"))))]
3136    backtrace: Option<()>,
3137    /// Contexts providing additional information about the error.
3138    contexts: Vec<YoContext>,
3139    /// A unique identifier for this error instance.
3140    instance_id: u32,
3141    /// Timestamp when the error was created (only available with `std` feature).
3142    #[cfg(feature = "std")]
3143    #[allow(dead_code)]
3144    created_at: SystemTime,
3145}
3146
3147impl Clone for Yoshi {
3148    /// Creates a clone of the `Yoshi` error.
3149    ///
3150    /// Note: In `std` mode, the backtrace is not cloned (as `std::backtrace::Backtrace`
3151    /// doesn't implement `Clone`). Instead, the clone will have no backtrace (`None`).
3152    /// In `no_std` mode, the backtrace is properly cloned as it only contains basic
3153    /// location information.
3154    ///
3155    /// A new unique instance ID is generated for the clone to maintain error tracking.
3156    fn clone(&self) -> Self {
3157        let instance_id = ERROR_INSTANCE_COUNTER.fetch_add(1, Ordering::Relaxed);
3158
3159        Self {
3160            kind: self.kind.clone(),
3161            #[cfg(feature = "std")]
3162            backtrace: None, // Cannot clone std::backtrace::Backtrace, so set to None
3163            #[cfg(not(feature = "std"))]
3164            backtrace: self.backtrace.clone(), // YoshiBacktrace implements Clone in no_std mode
3165            contexts: self.contexts.clone(),
3166            instance_id,
3167            #[cfg(feature = "std")]
3168            created_at: SystemTime::now(), // Use current time for the clone
3169        }
3170    }
3171}
3172
3173impl Yoshi {
3174    /// Creates a new `Yoshi` error with optimized allocation and monitoring.
3175    ///
3176    /// This is the primary constructor for `Yoshi` errors. It increments
3177    /// a global instance counter and, if the `std` feature is enabled and
3178    /// backtraces are enabled via environment variables (`RUST_BACKTRACE`
3179    /// or `RUST_LIB_BACKTRACE`), it captures a backtrace.
3180    ///
3181    /// # Arguments
3182    ///
3183    /// * `kind` - The [`YoshiKind`] that categorizes this error.
3184    ///
3185    /// # Returns
3186    ///
3187    /// A new `Yoshi` error instance.
3188    ///
3189    /// # Examples
3190    ///
3191    /// ```
3192    /// use yoshi_std::{Yoshi, YoshiKind};
3193    ///
3194    /// let err = Yoshi::new(YoshiKind::NotFound {
3195    ///     resource_type: "User".into(),
3196    ///     identifier: "john.doe".into(),
3197    ///     search_locations: None,
3198    /// });
3199    ///
3200    /// assert!(matches!(err.kind(), YoshiKind::NotFound { .. }));
3201    /// ```
3202    #[inline]
3203    pub fn new(kind: YoshiKind) -> Self {
3204        let instance_id = ERROR_INSTANCE_COUNTER.fetch_add(1, Ordering::Relaxed);
3205
3206        Self {
3207            kind,
3208            #[cfg(feature = "std")]
3209            backtrace: capture_bt(),
3210            #[cfg(not(feature = "std"))]
3211            backtrace: None,
3212            contexts: Vec::with_capacity(4), // Pre-allocate for typical error chain depth
3213            instance_id,
3214            #[cfg(feature = "std")]
3215            created_at: SystemTime::now(),
3216        }
3217    }
3218
3219    /// Creates a new `Yoshi` error by wrapping a foreign `Error` trait object.
3220    ///
3221    /// This is an explicit conversion for generic error types, allowing them
3222    /// to be integrated into the `Yoshi` error chain without requiring a
3223    /// blanket `From` implementation that might conflict or cause issues
3224    /// with unstable features.
3225    /// The type name of the wrapped error is captured for diagnostic purposes.
3226    ///
3227    /// # Type Parameters
3228    ///
3229    /// * `E` - The type of the foreign error, which must implement `Error`,
3230    ///   `Send`, `Sync`, and have a `'static` lifetime.
3231    ///
3232    /// # Arguments
3233    ///
3234    /// * `e` - The foreign error instance to wrap.
3235    ///
3236    /// # Returns
3237    ///
3238    /// A new `Yoshi` error with its kind to `YoshiKind::Foreign`.
3239    ///
3240    /// # Examples
3241    ///
3242    /// ```
3243    /// use std::io;
3244    /// use yoshi_std::{Yoshi, YoshiKind};
3245    ///
3246    /// #[derive(Debug)]
3247    /// struct CustomError;
3248    /// impl std::fmt::Display for CustomError {
3249    ///     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3250    ///         write!(f, "a custom error occurred")
3251    ///     }
3252    /// }
3253    /// impl std::error::Error for CustomError {}
3254    ///
3255    /// let io_error = io::Error::new(io::ErrorKind::Other, "disk full");
3256    /// let yoshi_io_error = Yoshi::foreign(io_error);
3257    /// assert!(matches!(yoshi_io_error.kind(), YoshiKind::Foreign { .. }));
3258    /// println!("Wrapped IO error: {}", yoshi_io_error);
3259    ///
3260    /// let custom_error = CustomError;
3261    /// let yoshi_custom_error = Yoshi::foreign(custom_error);
3262    /// assert!(matches!(yoshi_custom_error.kind(), YoshiKind::Foreign { .. }));
3263    /// println!("Wrapped custom error: {}", yoshi_custom_error);
3264    /// ```
3265    #[inline]
3266    #[track_caller]
3267    pub fn foreign<E>(e: E) -> Self
3268    where
3269        E: Error + Send + Sync + 'static,
3270    {
3271        Self::new(YoshiKind::from_foreign_with_context(e, ""))
3272    }
3273
3274    /// Gets the unique instance ID for debugging and correlation.
3275    ///
3276    /// Each `Yoshi` error instance is assigned a unique `u64` ID upon creation.
3277    /// This ID can be used to track specific error occurrences in logs or
3278    /// telemetry systems, especially in highly concurrent environments.
3279    ///
3280    /// # Returns
3281    ///
3282    /// The unique instance ID of this `Yoshi` error.
3283    ///
3284    /// # Examples
3285    ///
3286    /// ```
3287    /// # use yoshi_std::{Yoshi, YoshiKind};
3288    /// let err1 = Yoshi::new(YoshiKind::Internal {
3289    ///     message: "test".into(),
3290    ///     source: None,
3291    ///     component: None,
3292    /// });
3293    /// let err2 = Yoshi::new(YoshiKind::Internal {
3294    ///     message: "test".into(),
3295    ///     source: None,
3296    ///     component: None,
3297    /// });
3298    ///
3299    /// assert_ne!(err1.instance_id(), err2.instance_id());
3300    /// println!("Error 1 ID: {}", err1.instance_id());
3301    /// println!("Error 2 ID: {}", err2.instance_id());
3302    /// ```
3303    #[inline]
3304    pub const fn instance_id(&self) -> u32 {
3305        self.instance_id
3306    }
3307
3308    /// Returns a reference to the `YoshiKind` of this error.
3309    ///
3310    /// This allows inspecting the high-level classification of the error
3311    /// and accessing its specific fields.
3312    ///
3313    /// # Returns
3314    ///
3315    /// A constant reference to the [`YoshiKind`] enum variant.
3316    ///
3317    /// # Examples
3318    ///
3319    /// ```
3320    /// use yoshi_std::{Yoshi, YoshiKind};
3321    ///
3322    /// let err = Yoshi::new(YoshiKind::NotFound {
3323    ///     resource_type: "User".into(),
3324    ///     identifier: "john.doe".into(),
3325    ///     search_locations: None,
3326    /// });
3327    ///
3328    /// match err.kind() {
3329    ///     YoshiKind::NotFound { identifier, .. } => {
3330    ///         println!("User not found: {}", identifier);
3331    ///     }
3332    ///     _ => (),
3333    /// }
3334    /// ```
3335    #[inline]
3336    pub const fn kind(&self) -> &YoshiKind {
3337        &self.kind
3338    }
3339
3340    /// Gets the error severity level (0-100).
3341    ///
3342    /// This is a convenience method that delegates to `self.kind().severity()`.
3343    ///
3344    /// # Returns
3345    ///
3346    /// A `u8` value indicating the severity of the error.
3347    ///
3348    /// # Examples
3349    ///
3350    /// ```
3351    /// # use yoshi_std::YoshiKind;
3352    /// let internal_error = YoshiKind::Internal {
3353    ///     message: "simulated error".into(),
3354    ///     source: None,
3355    ///     component: None,
3356    /// };
3357    /// assert_eq!(internal_error.severity(), 80);
3358    ///
3359    /// let validation_error = YoshiKind::Validation {
3360    ///     field: "email".into(),
3361    ///     message: "Invalid format".into(),
3362    ///     expected: None,
3363    ///     actual: None,
3364    /// };
3365    /// assert_eq!(validation_error.severity(), 20);
3366    /// ```
3367    #[inline]
3368    pub const fn severity(&self) -> u8 {
3369        self.kind.severity()
3370    }
3371
3372    /// Checks if this is a transient error that might succeed on retry.
3373    ///
3374    /// This is a convenience method that delegates to `self.kind().is_transient()`.
3375    ///
3376    /// # Returns
3377    ///
3378    /// `true` if the error's kind is considered transient, `false` otherwise.
3379    ///
3380    /// # Examples
3381    ///
3382    /// ```
3383    /// # use yoshi_std::YoshiKind;
3384    /// # use core::time::Duration;
3385    /// let timeout_error = YoshiKind::Timeout {
3386    ///     operation: "API call".into(),
3387    ///     duration: Duration::from_secs(10),
3388    ///     expected_max: None,
3389    /// };
3390    /// assert!(timeout_error.is_transient());
3391    ///
3392    /// let config_error = YoshiKind::Config {
3393    ///     message: "Missing key".into(),
3394    ///     source: None,
3395    ///     config_path: None,
3396    /// };
3397    /// assert!(!config_error.is_transient());
3398    /// ```
3399    #[inline]
3400    pub const fn is_transient(&self) -> bool {
3401        self.kind.is_transient()
3402    }
3403
3404    /// Adds a context message to the error.
3405    ///
3406    /// This method enhances the error with additional diagnostic information,
3407    /// making it easier to trace the origin and propagation of failures.
3408    ///
3409    /// # Arguments
3410    ///
3411    /// * `msg` - The context message. It can be any type that converts into a `String`.
3412    ///
3413    /// # Returns
3414    ///
3415    /// The modified `Yoshi` error instance with the new context.
3416    ///
3417    /// # Examples
3418    ///
3419    /// ```
3420    /// use yoshi_std::{Yoshi, YoshiKind};
3421    ///
3422    /// let err = Yoshi::new(YoshiKind::Internal {
3423    ///     message: "database query failed".into(),
3424    ///     source: None,
3425    ///     component: None,
3426    /// })
3427    /// .context("Attempting to fetch user data");
3428    ///
3429    /// println!("Error: {}", err);
3430    /// ```
3431    ///
3432    /// # Panics
3433    ///
3434    /// This method may panic if context storage fails, though this is extremely unlikely.
3435    #[track_caller]
3436    #[inline]
3437    #[must_use]
3438    pub fn context(mut self, msg: impl Into<String>) -> Self {
3439        self.contexts
3440            .push(YoContext::new(msg).with_location(yoshi_location!()));
3441        self
3442    }
3443
3444    /// Adds a suggestion to the error's primary context.
3445    ///
3446    /// This method adds a human-readable suggestion to the current `Yoshi` error.
3447    /// The suggestion is stored in the primary (most recent) context associated
3448    /// with this error.
3449    ///
3450    /// # Arguments
3451    ///
3452    /// * `s` - The suggestion message. It can be any type that converts into a `String`.
3453    ///
3454    /// # Returns
3455    ///
3456    /// The modified `Yoshi` error instance with the new suggestion.
3457    ///
3458    /// # Examples
3459    ///
3460    /// ```
3461    /// use yoshi_std::{Yoshi, YoshiKind};
3462    ///    /// let err = Yoshi::new(YoshiKind::Io(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "file access denied")))
3463    ///     .with_suggestion("Check file permissions or path.");
3464    ///
3465    /// assert!(err.suggestion().as_deref() == Some("Check file permissions or path."));
3466    /// ```
3467    ///
3468    /// # Panics
3469    ///
3470    /// This method may panic if the context storage fails, though this is extremely unlikely.
3471    #[inline]
3472    #[track_caller]
3473    #[must_use]
3474    pub fn with_suggestion(mut self, s: impl Into<String>) -> Self {
3475        // Ensure there's at least one context to attach the suggestion to
3476        if self.contexts.is_empty() {
3477            self.contexts
3478                .push(YoContext::new("Error occurred").with_location(yoshi_location!()));
3479        }
3480        self.contexts
3481            .last_mut()
3482            .expect("contexts should not be empty")
3483            .suggestion = Some(intern_string(s.into()));
3484        self
3485    }
3486
3487    /// Attaches a component identifier to the error's primary context.
3488    ///
3489    /// This method adds a component identifier to help categorize and trace
3490    /// errors within different parts of a system or application. The component
3491    /// information is stored as metadata with the key "component".
3492    ///
3493    /// # Arguments
3494    ///
3495    /// * `component` - The component identifier. It can be any type that converts into a `String`.
3496    ///
3497    /// # Returns
3498    ///
3499    /// The modified `Yoshi` error instance with the component information.
3500    ///
3501    /// # Examples
3502    ///
3503    /// ```
3504    /// use yoshi_std::{Yoshi, YoshiKind};
3505    ///
3506    /// let err = Yoshi::new(YoshiKind::Internal {
3507    ///     message: "operation failed".into(),
3508    ///     source: None,
3509    ///     component: None,
3510    /// })
3511    /// .with_component("database");
3512    ///
3513    /// // Component can be retrieved from metadata
3514    /// let ctx = err.primary_context().unwrap();
3515    /// assert_eq!(ctx.metadata.get("component").map(|s| s.as_ref()), Some("database"));
3516    /// ```
3517    ///
3518    /// # Panics
3519    ///
3520    /// This method may panic if the context storage fails, though this is extremely unlikely.
3521    #[inline]
3522    #[track_caller]
3523    #[must_use]
3524    pub fn with_component(mut self, component: impl Into<String>) -> Self {
3525        // Ensure there's at least one context to attach the component to
3526        if self.contexts.is_empty() {
3527            self.contexts
3528                .push(YoContext::new("Error occurred").with_location(yoshi_location!()));
3529        }
3530        self.contexts
3531            .last_mut()
3532            .expect("contexts should not be empty")
3533            .metadata
3534            .insert(intern_string("component"), intern_string(component.into()));
3535        self
3536    }
3537
3538    /// Attaches a typed shell to the error's primary context.
3539    ///
3540    /// This method allows embedding arbitrary Rust types within the error's context.
3541    /// This is useful for passing structured, type-safe debugging information
3542    /// that can be retrieved later using `shell::<T>()`.
3543    ///
3544    /// # Arguments
3545    ///
3546    /// * `shell` - The data to attach. It must implement `Any`, `Send`, `Sync`, and have a `'static` lifetime.
3547    ///
3548    /// # Returns
3549    ///
3550    /// The modified `Yoshi` error instance with the new shell.
3551    ///
3552    /// # Examples
3553    ///
3554    /// ```
3555    /// use yoshi_std::{Yoshi, YoshiKind};
3556    ///
3557    /// #[derive(Debug, PartialEq)]
3558    /// struct RequestContext {
3559    ///     user_id: u64,
3560    ///     request_path: String,
3561    /// }
3562    ///
3563    /// let err = Yoshi::new(YoshiKind::Internal {
3564    ///     message: "handler failed".into(),
3565    ///     source: None,
3566    ///     component: None,
3567    /// })
3568    /// .with_shell(RequestContext { user_id: 123, request_path: "/api/data".to_string() });
3569    ///
3570    /// let ctx_payload = err.shell::<RequestContext>().unwrap();
3571    /// assert_eq!(ctx_payload.user_id, 123);
3572    /// ```
3573    ///
3574    /// # Panics
3575    ///
3576    /// This method may panic if the shell storage fails, though this is extremely unlikely.
3577    #[inline]
3578    #[track_caller]
3579    #[must_use]
3580    pub fn with_shell(mut self, shell: impl Any + Send + Sync + 'static) -> Self {
3581        // Ensure there's at least one context to attach the shell to
3582        if self.contexts.is_empty() {
3583            self.contexts
3584                .push(YoContext::new("Error occurred").with_location(yoshi_location!()));
3585        }
3586        self.contexts
3587            .last_mut()
3588            .expect("contexts should not be empty")
3589            .add_shell_in_place(shell);
3590        self
3591    }
3592
3593    /// Sets the priority for the error's primary context.
3594    ///
3595    /// Priority can be used to indicate the relative importance of a context
3596    /// message, influencing how errors are logged or processed by error handling
3597    /// systems. Higher values indicate higher priority.
3598    ///
3599    /// # Arguments
3600    ///
3601    /// * `priority` - The priority level (0-255).
3602    ///
3603    /// # Returns
3604    ///
3605    /// The modified `Yoshi` error instance with the updated priority.
3606    ///
3607    /// # Examples
3608    /// ```
3609    /// use yoshi_std::{Yoshi, YoshiKind};
3610    ///
3611    /// let err = Yoshi::new(YoshiKind::Internal {
3612    ///     message: "critical failure".into(),
3613    ///     source: None,
3614    ///     component: None,
3615    /// })
3616    /// .with_priority(250); // Highest priority
3617    ///
3618    /// assert_eq!(err.primary_context().unwrap().priority, 250);
3619    /// ```
3620    ///
3621    /// # Panics
3622    ///
3623    /// This method ensures that there is at least one context before updating priority.
3624    /// If no contexts exist, it creates one automatically, so this method should not panic.
3625    #[inline]
3626    #[must_use]
3627    #[track_caller]
3628    pub fn with_priority(mut self, priority: u8) -> Self {
3629        // Ensure there's at least one context to update
3630        if self.contexts.is_empty() {
3631            self.contexts
3632                .push(YoContext::new("Error occurred").with_location(yoshi_location!()));
3633        }
3634        self.contexts
3635            .last_mut()
3636            .expect("contexts should not be empty")
3637            .priority = priority;
3638        self
3639    }
3640
3641    /// Adds metadata to the error's primary context.
3642    ///
3643    /// Metadata are key-value pairs that provide additional, unstructured
3644    /// diagnostic information. These can be used for logging, filtering,
3645    /// or passing arbitrary data alongside the error.
3646    ///
3647    /// # Arguments
3648    ///
3649    /// * `k` - The metadata key. It can be any type that converts into a `String`.
3650    /// * `v` - The metadata value. It can be any type that converts into a `String`.
3651    ///
3652    /// # Returns
3653    ///
3654    /// The modified `Yoshi` error instance with the new metadata.
3655    ///
3656    /// # Examples
3657    ///
3658    /// ```
3659    /// use yoshi_std::{Yoshi, YoshiKind, Arc};
3660    ///
3661    /// let err = Yoshi::new(YoshiKind::Internal {
3662    ///     message: "cache read failed".into(),
3663    ///     source: None,
3664    ///     component: None,
3665    /// })
3666    /// .with_metadata("cache_key", "user_profile_123")
3667    /// .with_metadata("region", "us-east-1");
3668    ///
3669    /// let metadata = &err.primary_context().unwrap().metadata;
3670    /// assert_eq!(metadata.get(&Arc::from("cache_key")).map(|s| s.as_ref()), Some("user_profile_123"));
3671    /// assert_eq!(metadata.get(&Arc::from("region")).map(|s| s.as_ref()), Some("us-east-1"));
3672    /// ```
3673    ///
3674    /// # Panics
3675    ///
3676    /// This method may panic if metadata storage fails, though this is extremely unlikely.
3677    #[inline]
3678    #[track_caller]
3679    #[must_use]
3680    pub fn with_metadata(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
3681        // Ensure there's at least one context to attach metadata to
3682        if self.contexts.is_empty() {
3683            self.contexts
3684                .push(YoContext::new("Error occurred").with_location(yoshi_location!()));
3685        }
3686        self.contexts
3687            .last_mut()
3688            .expect("contexts should not be empty")
3689            .metadata
3690            .insert(intern_string(k.into()), intern_string(v.into()));
3691        self
3692    }
3693
3694    /// Sets location information on the error's primary context.
3695    ///
3696    /// This method attaches source code location information to the error's primary context,
3697    /// helping with debugging and error tracing. It consumes `self` and returns a modified `Self`.
3698    ///
3699    /// # Arguments
3700    ///
3701    /// * `location` - The `YoshiLocation` to set.
3702    ///
3703    /// # Returns
3704    ///
3705    /// The modified `Yoshi` error instance with the location set.
3706    ///
3707    /// # Examples
3708    ///
3709    /// ```
3710    /// use yoshi_std::{Yoshi, YoshiKind, YoshiLocation};
3711    ///
3712    /// let location = YoshiLocation::new("src/main.rs", 10, 5);
3713    /// let err = Yoshi::new(YoshiKind::Internal {
3714    ///     message: "operation failed".into(),
3715    ///     source: None,
3716    ///     component: None,
3717    /// })
3718    /// .with_location(location);
3719    ///
3720    /// assert_eq!(err.primary_context().unwrap().location.unwrap().file, "src/main.rs");
3721    /// assert_eq!(err.primary_context().unwrap().location.unwrap().line, 10);
3722    /// ```
3723    ///
3724    /// # Panics
3725    ///
3726    /// This method may panic if location storage fails, though this is extremely unlikely.
3727    #[inline]
3728    #[track_caller]
3729    #[must_use]
3730    pub fn with_location(mut self, location: YoshiLocation) -> Self {
3731        // Ensure there's at least one context to attach location to
3732        if self.contexts.is_empty() {
3733            self.contexts
3734                .push(YoContext::new("Error occurred").with_location(yoshi_location!()));
3735        }
3736        self.contexts
3737            .last_mut()
3738            .expect("contexts should not be empty")
3739            .location = Some(location);
3740        self
3741    }
3742
3743    /// Returns a reference to the optional backtrace.
3744    ///
3745    /// The backtrace is only available when the `std` feature is enabled and
3746    /// `RUST_BACKTRACE` or `RUST_LIB_BACKTRACE` environment variables are set.
3747    ///
3748    /// # Returns
3749    ///
3750    /// An `Option` containing a reference to the [`YoshiBacktrace`] if available,
3751    /// otherwise `None`.
3752    ///
3753    /// # Examples
3754    ///
3755    /// ```no_run
3756    /// # #[cfg(feature = "std")] {
3757    /// # use yoshi_std::{Yoshi, YoshiKind};
3758    /// # std::env::set_var("RUST_BACKTRACE", "1");
3759    /// let err = Yoshi::new(YoshiKind::Internal {
3760    ///     message: "test error".into(),
3761    ///     source: None,
3762    ///     component: None,
3763    /// });
3764    /// if let Some(bt) = err.backtrace() {
3765    ///     println!("Backtrace: {}", bt);
3766    /// }
3767    /// # }
3768    /// ```
3769    #[cfg(feature = "std")]
3770    #[inline]
3771    pub const fn backtrace(&self) -> Option<&YoshiBacktrace> {
3772        self.backtrace.as_ref()
3773    }
3774
3775    /// Returns a reference to the underlying foreign error (if `YoshiKind::Foreign`).
3776    ///
3777    /// This method allows downcasting the boxed `dyn Error` contained within a
3778    /// `YoshiKind::Foreign` variant to a concrete type.
3779    ///
3780    /// # Type Parameters
3781    ///
3782    /// * `T` - The concrete type to downcast to, which must implement `Error`.
3783    ///
3784    /// # Returns
3785    ///
3786    /// An `Option` containing a reference to the downcasted error of type `T`,
3787    /// or `None` if the error is not `YoshiKind::Foreign` or cannot be downcasted
3788    /// to the specified type.
3789    ///    /// # Examples
3790    ///
3791    /// ```
3792    /// use std::io;
3793    /// use yoshi_std::{Yoshi, YoshiKind};
3794    ///
3795    /// let io_err = io::Error::new(io::ErrorKind::NotFound, "file.txt not found");
3796    /// let yoshi_err = Yoshi::foreign(io_err);
3797    ///
3798    /// // Attempt to downcast to io::Error
3799    /// if let Some(err) = yoshi_err.downcast_ref::<io::Error>() {
3800    ///     assert_eq!(err.kind(), io::ErrorKind::NotFound);
3801    /// } else {
3802    ///     panic!("Expected io::Error");
3803    /// }
3804    /// ```
3805    #[inline]
3806    pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
3807        if let YoshiKind::Foreign { error, .. } = &self.kind {
3808            // First try to downcast the ForeignErrorWrapper itself to T
3809            if let Some(result) = error.downcast_ref::<T>() {
3810                return Some(result);
3811            }
3812
3813            // If that fails, try to downcast the wrapper to ForeignErrorWrapper
3814            // and then downcast its inner error to T
3815            if let Some(wrapper) = error.downcast_ref::<ForeignErrorWrapper>() {
3816                wrapper.inner.downcast_ref::<T>()
3817            } else {
3818                None
3819            }
3820        } else {
3821            None
3822        }
3823    }
3824
3825    /// Returns a mutable reference to the underlying foreign error (if `YoshiKind::Foreign`).
3826    ///
3827    /// This method allows mutable downcasting the boxed `dyn Error` contained within a
3828    /// `YoshiKind::Foreign` variant to a concrete type.
3829    ///
3830    /// # Type Parameters
3831    ///
3832    /// * `T` - The concrete type to downcast to, which must implement `Error`.
3833    ///
3834    /// # Returns
3835    ///    /// An `Option` containing a mutable reference to the downcasted error of type `T`,
3836    /// or `None` if the error is not `YoshiKind::Foreign` or cannot be downcasted
3837    /// to the specified type.
3838    #[inline]
3839    pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> {
3840        if let YoshiKind::Foreign { error, .. } = &mut self.kind {
3841            // Use a single downcast operation and then check both possibilities
3842            if error.is::<ForeignErrorWrapper>() {
3843                // If it's a ForeignErrorWrapper, get the inner error
3844                if let Some(wrapper) = error.downcast_mut::<ForeignErrorWrapper>() {
3845                    wrapper.inner.downcast_mut::<T>()
3846                } else {
3847                    None
3848                }
3849            } else {
3850                // If it's not a wrapper, try to downcast directly
3851                error.downcast_mut::<T>()
3852            }
3853        } else {
3854            None
3855        }
3856    }
3857
3858    /// Returns the primary context associated with this error.
3859    ///
3860    /// The primary context is typically the most recent or most relevant
3861    /// context added to the error, often containing the most specific
3862    /// information about the direct cause of the failure.
3863    ///
3864    /// # Returns
3865    ///
3866    /// An `Option` containing a reference to the primary `YoContext`,
3867    /// or `None` if no contexts have been added.
3868    ///
3869    /// # Examples
3870    ///
3871    /// ```
3872    /// use yoshi_std::{Yoshi, YoshiKind};
3873    ///
3874    /// let err = Yoshi::new(YoshiKind::Internal {
3875    ///     message: "failed step".into(),
3876    ///     source: None,
3877    ///     component: None,
3878    /// })
3879    /// .context("Step 1 failed")
3880    /// .context("Step 2 failed"); // This is the primary context
3881    ///
3882    /// assert_eq!(err.primary_context().unwrap().message.as_deref(), Some("Step 2 failed"));
3883    /// ```
3884    #[inline]
3885    pub fn primary_context(&self) -> Option<&YoContext> {
3886        self.contexts.last()
3887    }
3888
3889    /// Returns an iterator over all contexts associated with this error.
3890    ///
3891    /// Contexts are ordered from oldest (first added) to newest (most recent, primary).
3892    ///
3893    /// # Returns
3894    ///
3895    /// An iterator yielding references to `YoContext` instances.
3896    ///
3897    /// # Examples
3898    ///
3899    /// ```
3900    /// use yoshi_std::{Yoshi, YoshiKind};
3901    ///
3902    /// let err = Yoshi::new(YoshiKind::Internal {
3903    ///     message: "original error".into(),
3904    ///     source: None,
3905    ///     component: None,
3906    /// })
3907    /// .context("context 1")
3908    /// .context("context 2");
3909    ///
3910    /// let messages: Vec<_> = err.contexts().filter_map(|c| c.message.as_deref()).collect();
3911    /// assert_eq!(messages, vec!["context 1", "context 2"]);
3912    /// ```
3913    #[inline]
3914    pub fn contexts(&self) -> impl Iterator<Item = &YoContext> {
3915        self.contexts.iter()
3916    }
3917
3918    /// Returns the suggestion from the primary context, if any.
3919    ///
3920    /// This is a convenience method to quickly access the most relevant
3921    /// suggestion for resolving the error.
3922    ///
3923    /// # Returns
3924    ///
3925    /// An `Option` containing a reference to the suggestion string, or `None`.
3926    ///
3927    /// # Examples
3928    ///
3929    /// ```
3930    /// use yoshi_std::{Yoshi, YoshiKind};
3931    ///
3932    /// let err = Yoshi::new(YoshiKind::Io(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "file access denied")))
3933    ///     .with_suggestion("Check file permissions.");
3934    ///
3935    /// assert_eq!(err.suggestion().as_deref(), Some("Check file permissions."));
3936    /// ```
3937    #[inline]
3938    pub fn suggestion(&self) -> Option<&str> {
3939        self.primary_context()
3940            .and_then(|ctx| ctx.suggestion.as_deref())
3941    }
3942
3943    /// Returns a typed shell from the primary context, if any.
3944    ///
3945    /// This is a convenience method to quickly access a structured shell
3946    /// from the most relevant context.
3947    ///
3948    /// # Type Parameters
3949    ///
3950    /// * `T` - The type of shell to retrieve.
3951    ///
3952    /// # Returns
3953    ///
3954    /// An `Option` containing a reference to the shell of type `T`, or `None`.
3955    ///
3956    /// # Examples
3957    ///
3958    /// ```
3959    /// use yoshi_std::{Yoshi, YoshiKind};
3960    /// #[derive(Debug, PartialEq)]
3961    /// struct CustomPayload(u32);
3962    /// let err = Yoshi::new(YoshiKind::Internal {
3963    ///     message: "test".into(),
3964    ///     source: None,
3965    ///     component: None,
3966    /// })
3967    /// .with_shell(CustomPayload(123));
3968    ///    /// assert_eq!(err.shell::<CustomPayload>().unwrap().0, 123);
3969    /// ```
3970    #[inline]
3971    pub fn shell<T: 'static>(&self) -> Option<&T> {
3972        // Search ALL contexts for the shell, not just the primary context
3973        // This ensures payloads can be found regardless of context priority ordering
3974        for context in &self.contexts {
3975            if let Some(shell) = context.shell::<T>() {
3976                return Some(shell);
3977            }
3978        }
3979        None
3980    }
3981
3982    /// The nested error, equivalent to `source()`, but more thematically expressive.
3983    ///
3984    /// This method provides thematic access to the underlying error source while
3985    /// maintaining full backwards compatibility with the standard `Error` trait.
3986    ///
3987    /// # Returns
3988    ///
3989    /// An `Option` containing a reference to the nested error, or `None` if
3990    /// there is no underlying source.
3991    ///
3992    /// # Examples
3993    ///
3994    /// ```rust
3995    /// use yoshi_std::{Yoshi, YoshiKind};
3996    /// # use std::io;
3997    /// let inner = Yoshi::new(YoshiKind::Internal {
3998    ///     message: "inner failure".into(),
3999    ///     source: None,
4000    ///     component: None,
4001    /// });
4002    /// let outer = Yoshi::new(YoshiKind::Internal {
4003    ///     message: "outer failure".into(),
4004    ///     source: Some(Box::new(inner)),
4005    ///     component: None,
4006    /// });
4007    /// assert!(outer.nest().is_some());
4008    /// ```
4009    #[inline]
4010    pub fn nest(&self) -> Option<&(dyn Error + 'static)> {
4011        self.kind.source()
4012    }
4013
4014    /// The explanation or context attached to the error.
4015    ///
4016    /// This method provides direct access to the primary context message,
4017    /// offering a thematic alternative to accessing context information.
4018    ///
4019    /// # Returns
4020    ///
4021    /// An `Option` containing a reference to the laytext string, or `None`
4022    /// if no context message is available.
4023    ///
4024    /// # Examples
4025    ///
4026    /// ```rust
4027    /// use yoshi_std::{Yoshi, YoshiKind};
4028    /// let err = Yoshi::new(YoshiKind::Internal {
4029    ///     message: "base error".into(),
4030    ///     source: None,
4031    ///     component: None,
4032    /// })
4033    /// .context("operation failed");
4034    /// assert_eq!(err.laytext().unwrap(), "operation failed");
4035    /// ```
4036    #[inline]
4037    pub fn laytext(&self) -> Option<&str> {
4038        self.primary_context()
4039            .and_then(|ctx| ctx.message.as_deref())
4040    }
4041
4042    /// Adds contextual information using the thematic `.lay()` method.
4043    ///
4044    /// This method is equivalent to `.context()` but provides thematic naming
4045    /// consistent with the Hatch ecosystem's metaphorical framework.
4046    ///
4047    /// # Arguments
4048    ///
4049    /// * `msg` - The context message to attach.
4050    ///
4051    /// # Returns
4052    ///
4053    /// The modified `Yoshi` error instance with the new context.
4054    ///
4055    /// # Examples
4056    ///
4057    /// ```rust
4058    /// use yoshi_std::{Yoshi, YoshiKind};
4059    /// let err = Yoshi::new(YoshiKind::Internal {
4060    ///     message: "base error".into(),
4061    ///     source: None,
4062    ///     component: None,
4063    /// })
4064    /// .lay("while processing request");
4065    /// assert!(err.to_string().contains("while processing request"));
4066    /// ```
4067    #[track_caller]
4068    #[inline]
4069    #[must_use]
4070    pub fn lay(self, msg: impl Into<String>) -> Self {
4071        self.context(msg)
4072    }
4073
4074    /// Gathers analysis results about the contexts in this error.
4075    ///
4076    /// This method performs a quick scan of all attached contexts to provide
4077    /// aggregated statistics, useful for logging, analytics, or deciding
4078    /// on error handling strategies.
4079    ///
4080    /// # Returns
4081    ///
4082    /// A `ContextAnalysis` struct containing various metrics about the contexts.
4083    ///
4084    /// # Examples
4085    ///
4086    /// ```
4087    /// use yoshi_std::{Yoshi, YoshiKind, YoshiLocation};
4088    ///
4089    /// let err = Yoshi::new(YoshiKind::Internal {
4090    ///     message: "base error".into(),
4091    ///     source: None,
4092    ///     component: None,
4093    /// })
4094    /// .context("Intermediate step")
4095    /// .with_metadata("key", "value")
4096    /// .with_suggestion("Try again")
4097    /// .context("Final step failed")
4098    /// .with_location(YoshiLocation::new("src/main.rs", 10, 5));
4099    ///
4100    /// let analysis = err.analyze_contexts();
4101    /// assert_eq!(analysis.total_contexts, 2);
4102    /// assert_eq!(analysis.context_depth, 2);
4103    /// assert!(analysis.has_suggestions);
4104    /// assert!(analysis.has_location_info);
4105    /// assert_eq!(analysis.metadata_entries, 1);
4106    /// ```
4107    pub fn analyze_contexts(&self) -> ContextAnalysis {
4108        let mut analysis = ContextAnalysis {
4109            total_contexts: self.contexts.len(),
4110            context_depth: self.contexts.len(), // Simple depth = count for now
4111            ..ContextAnalysis::default()
4112        };
4113
4114        for ctx in &self.contexts {
4115            if ctx.suggestion.is_some() {
4116                analysis.has_suggestions = true;
4117            }
4118            if ctx.location.is_some() {
4119                analysis.has_location_info = true;
4120            }
4121            analysis.metadata_entries += ctx.metadata.len();
4122            analysis.typed_payloads += ctx.payloads.len();
4123
4124            // The primary context is the last one in the vector
4125            if let Some(primary_ctx) = self.contexts.last() {
4126                analysis.primary_context_priority = primary_ctx.priority;
4127            }
4128        }
4129        analysis
4130    }
4131}
4132
4133impl Display for Yoshi {
4134    /// Formats the `Yoshi` error for display with optimized O(n) error chain traversal.
4135    ///
4136    /// This implementation provides a comprehensive, human-readable representation
4137    /// of the error, designed for debugging and logging. It uses an optimized
4138    /// iterative approach to traverse error chains, eliminating the O(n²) performance
4139    /// bottleneck present in recursive formatting. The formatter collects the entire
4140    /// error chain first, then renders all information in a single linear pass.
4141    ///
4142    /// # Performance Characteristics
4143    ///
4144    /// - **Time Complexity**: O(n) where n is the total depth of the error chain
4145    /// - **Space Complexity**: O(n) for temporary chain storage
4146    /// - **Memory Allocation**: Minimized through `OptimizedFormatBuffer` usage
4147    /// - **Scaling**: Linear performance even for deep error chains (100+ levels)
4148    ///
4149    /// # Arguments
4150    ///
4151    /// * `f` - The formatter to write into.
4152    ///
4153    /// # Returns
4154    ///
4155    /// A `fmt::Result` indicating success or failure of the formatting.
4156    ///
4157    /// # Examples
4158    ///
4159    /// ```
4160    /// # use yoshi_std::{Yoshi, YoshiKind};
4161    /// let error = Yoshi::new(YoshiKind::Internal {
4162    ///     message: "Operation failed".into(),
4163    ///     source: None,
4164    ///     component: None,
4165    /// })
4166    /// .context("While processing request");
4167    ///
4168    /// println!("{}", error); // Efficient O(n) formatting
4169    /// ```
4170    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
4171        // Use optimized buffer for efficient string building
4172        let mut buffer = OptimizedFormatBuffer::new();
4173
4174        // Write primary error information
4175        buffer.append_optimized(&format!("{}: {}", self.instance_id, self.kind));
4176        buffer.append_optimized("\n");
4177
4178        // Print contexts from oldest to newest (excluding auto-generated ones)
4179        for (i, ctx) in self.contexts.iter().enumerate() {
4180            if i == 0
4181                && ctx.message.as_deref() == Some("Error occurred")
4182                && ctx.metadata.is_empty()
4183                && ctx.suggestion.is_none()
4184                && ctx.payloads.is_empty()
4185            {
4186                // Skip auto-generated default context if it provides no actual info
4187                continue;
4188            }
4189
4190            if let Some(msg) = ctx.message.as_deref() {
4191                buffer.append_optimized("Caused by: ");
4192                buffer.append_optimized(msg);
4193                buffer.append_optimized("\n");
4194            }
4195
4196            if !ctx.metadata.is_empty() {
4197                buffer.append_optimized("Metadata:\n");
4198                for (k, v) in &ctx.metadata {
4199                    buffer.append_optimized("  ");
4200                    buffer.append_optimized(k.as_ref());
4201                    buffer.append_optimized(": ");
4202                    buffer.append_optimized(v.as_ref());
4203                    buffer.append_optimized("\n");
4204                }
4205            }
4206
4207            if let Some(suggestion) = ctx.suggestion.as_deref() {
4208                buffer.append_optimized("Suggestion: ");
4209                buffer.append_optimized(suggestion);
4210                buffer.append_optimized("\n");
4211            }
4212
4213            if let Some(location) = ctx.location {
4214                buffer.append_optimized("Location: ");
4215                buffer.append_optimized(&location.to_string());
4216                buffer.append_optimized("\n");
4217            }
4218        }
4219
4220        // Collect complete error chain iteratively (O(n) instead of O(n²))
4221        let mut error_chain = Vec::new();
4222        let mut yoshi_contexts = Vec::new();
4223
4224        // Start with the source from this error's kind
4225        let mut current_error = self.kind.source();
4226
4227        while let Some(source_error) = current_error {
4228            // Check if it's a Yoshi error to extract contexts
4229            if let Some(yoshi_source) = source_error.downcast_ref::<Yoshi>() {
4230                // Add the Yoshi error's kind to the chain
4231                error_chain.push(format!("Caused by: {}", yoshi_source.kind));
4232
4233                // Collect contexts from this Yoshi error
4234                for ctx in &yoshi_source.contexts {
4235                    if let Some(msg) = ctx.message.as_deref() {
4236                        yoshi_contexts.push(format!("Caused by: {msg}"));
4237                    }
4238                }
4239
4240                // Move to the next error in the chain
4241                current_error = yoshi_source.kind.source();
4242            } else {
4243                // For non-Yoshi sources, add directly to chain and stop
4244                error_chain.push(format!("Caused by: {source_error}"));
4245                current_error = source_error.source();
4246            }
4247        }
4248
4249        // Append all collected error chain information
4250        for error_msg in error_chain {
4251            buffer.append_optimized(&error_msg);
4252            buffer.append_optimized("\n");
4253        }
4254
4255        // Append all collected Yoshi contexts
4256        for ctx_msg in yoshi_contexts {
4257            buffer.append_optimized(&ctx_msg);
4258            buffer.append_optimized("\n");
4259        }
4260
4261        // Add backtrace if available
4262        #[cfg(feature = "std")]
4263        if let Some(bt) = &self.backtrace {
4264            buffer.append_optimized("\nBacktrace:\n");
4265            buffer.append_optimized(&bt.to_string());
4266        }
4267
4268        // Write the complete formatted output
4269        write!(f, "{}", buffer.as_str().trim_end())
4270    }
4271}
4272
4273#[cfg(feature = "std")]
4274impl Error for Yoshi {
4275    /// Returns the underlying source of this error.
4276    ///
4277    /// This method provides access to the root cause of the error chain,
4278    /// enabling compatibility with Rust's standard error handling mechanisms.
4279    ///
4280    /// # Returns
4281    ///
4282    /// An `Option` containing a reference to the `dyn Error` source,
4283    /// or `None` if there is no underlying cause.
4284    fn source(&self) -> Option<&(dyn Error + 'static)> {
4285        self.kind.source()
4286    }
4287}
4288
4289#[cfg(not(feature = "std"))]
4290impl Error for Yoshi {
4291    /// Returns the underlying source of this error.
4292    ///
4293    /// This method provides access to the root cause of the error chain,
4294    /// enabling compatibility with Rust's standard error handling mechanisms.
4295    ///
4296    /// # Returns
4297    ///
4298    /// An `Option` containing a reference to the `dyn Error` source,
4299    /// or `None` if there is no underlying cause.
4300    fn source(&self) -> Option<&(dyn Error + 'static)> {
4301        self.kind.source()
4302    }
4303}
4304
4305impl From<String> for Yoshi {
4306    /// Converts a `String` into a `Yoshi` error.
4307    ///
4308    /// The string message is wrapped in an `Internal` `YoshiKind`.
4309    ///
4310    /// # Arguments
4311    ///
4312    /// * `s` - The string message to convert.
4313    ///
4314    /// # Returns
4315    ///
4316    /// A new `Yoshi` error instance.
4317    #[track_caller]
4318    fn from(s: String) -> Self {
4319        Yoshi::new(YoshiKind::Internal {
4320            message: s.into(),
4321            source: None,
4322            component: None,
4323        })
4324    }
4325}
4326
4327impl From<&str> for Yoshi {
4328    /// Converts a string slice (`&str`) into a `Yoshi` error.
4329    ///
4330    /// The string slice is converted to a `String` and then wrapped in an
4331    /// `Internal` `YoshiKind`.
4332    ///
4333    /// # Arguments
4334    ///
4335    /// * `s` - The string slice to convert.
4336    ///
4337    /// # Returns
4338    ///
4339    /// A new `Yoshi` error instance.
4340    #[track_caller]
4341    fn from(s: &str) -> Self {
4342        Yoshi::new(YoshiKind::Internal {
4343            message: s.to_string().into(),
4344            source: None,
4345            component: None,
4346        })
4347    }
4348}
4349
4350#[cfg(feature = "std")]
4351impl From<std::io::Error> for Yoshi {
4352    /// Converts a `std::io::Error` into a `Yoshi` error.
4353    ///
4354    /// The I/O error is wrapped in a `YoshiKind::Io` variant.
4355    ///
4356    /// # Arguments
4357    ///
4358    /// * `e` - The `std::io::Error` to convert.
4359    ///
4360    /// # Returns
4361    ///
4362    /// A new `Yoshi` error instance.
4363    #[track_caller]
4364    fn from(e: std::io::Error) -> Self {
4365        Yoshi::new(YoshiKind::Io(e))
4366    }
4367}
4368
4369#[cfg(not(feature = "std"))]
4370impl From<NoStdIo> for Yoshi {
4371    /// Converts a `NoStdIo` error into a `Yoshi` error.
4372    ///
4373    /// The `NoStdIo` error is wrapped in a `YoshiKind::Io` variant.
4374    ///
4375    /// # Arguments
4376    ///
4377    /// * `e` - The `NoStdIo` error to convert.
4378    ///
4379    /// # Returns
4380    ///
4381    /// A new `Yoshi` error instance.
4382    #[track_caller]
4383    fn from(e: NoStdIo) -> Self {
4384        Yoshi::new(YoshiKind::Io(e))
4385    }
4386}
4387
4388impl<T, E> HatchExt<T> for core::result::Result<T, E>
4389where
4390    E: Into<Yoshi> + Send + Sync + 'static, // Updated trait bounds
4391{
4392    #[track_caller]
4393    #[inline]
4394    fn context(self, msg: impl Into<String>) -> Result<T> {
4395        self.map_err(|e| e.into().context(msg))
4396    }
4397
4398    #[track_caller]
4399    #[inline]
4400    fn with_suggestion(self, s: impl Into<String>) -> Result<T> {
4401        self.map_err(|e| e.into().with_suggestion(s))
4402    }
4403    #[track_caller]
4404    #[inline]
4405    fn with_shell(self, p: impl Any + Send + Sync + 'static) -> Result<T> {
4406        self.map_err(|e| {
4407            let mut yoshi_err = e.into();
4408            // Ensure we have a context to attach the shell to with standard priority
4409            if yoshi_err.contexts.is_empty() {
4410                yoshi_err
4411                    .contexts
4412                    .push(YoContext::default().with_priority(128));
4413            }
4414            yoshi_err.with_shell(p)
4415        })
4416    }
4417
4418    /// Sets the priority for the error's primary context.
4419    #[track_caller]
4420    #[inline]
4421    fn with_priority(self, priority: u8) -> Result<T> {
4422        self.map_err(|e| e.into().with_priority(priority))
4423    }
4424
4425    // NEW: Short aliases - just delegate to the full methods
4426    #[track_caller]
4427    #[inline]
4428    fn ctx(self, msg: impl Into<String>) -> Result<T> {
4429        self.context(msg)
4430    }
4431
4432    #[track_caller]
4433    #[inline]
4434    fn help(self, s: impl Into<String>) -> Result<T> {
4435        self.with_suggestion(s)
4436    }
4437
4438    #[track_caller]
4439    #[inline]
4440    fn meta(self, k: impl Into<String>, v: impl Into<String>) -> Result<T> {
4441        self.map_err(|e| {
4442            let mut yoshi_err = e.into();
4443            // Ensure we have a context to attach metadata to with proper priority
4444            if yoshi_err.contexts.is_empty() {
4445                yoshi_err
4446                    .contexts
4447                    .push(YoContext::default().with_priority(128));
4448            }
4449            yoshi_err.with_metadata(k, v)
4450        })
4451    }
4452}
4453
4454/// Trait that adds `.lay(...)` to `Result<T, Yoshi>`, enriching errors with context.
4455///
4456/// This trait provides ergonomic context attachment using thematic naming that
4457/// aligns with the Yoshi metaphorical framework. The `.lay()` method is equivalent
4458/// to adding context but uses intuitive, game-inspired terminology.
4459///
4460/// # Performance Characteristics
4461///
4462/// - **Context Addition**: O(1) operation with minimal memory allocation
4463/// - **String Interning**: Automatic optimization for repeated context messages
4464/// - **Memory Efficiency**: Shared storage for common context patterns
4465///
4466/// # Examples
4467///
4468/// ```rust
4469/// use yoshi_std::{Hatch, LayContext, Yoshi, YoshiKind};
4470///
4471/// fn database_operation() -> Hatch<String> {
4472///     Err(Yoshi::new(YoshiKind::Internal {
4473///         message: "connection failed".into(),
4474///         source: None,
4475///         component: None,
4476///     }))
4477///     .lay("While establishing database connection")
4478/// }
4479/// ```
4480pub trait LayContext<T> {
4481    /// Adds a contextual message to the error chain, like laying an egg with metadata.
4482    ///
4483    /// This method enriches error information by attaching descriptive context
4484    /// that helps with debugging and error tracing. It uses thematic naming
4485    /// inspired by Yoshi's egg-laying ability to create memorable, intuitive APIs.
4486    ///
4487    /// # Arguments
4488    ///
4489    /// * `message` - The context message to attach. Accepts any type that converts to `String`.
4490    ///
4491    /// # Returns
4492    ///
4493    /// A `Hatch<T>` with the enriched context information attached.
4494    ///
4495    /// # Performance
4496    ///
4497    /// - **Time Complexity**: O(1) for context attachment
4498    /// - **Memory Optimization**: Automatic string interning for efficiency
4499    /// - **Allocation Pattern**: Minimal heap allocation with shared storage
4500    ///
4501    /// # Examples
4502    ///
4503    /// ```rust
4504    /// use yoshi_std::{Hatch, LayContext, Yoshi, YoshiKind};
4505    ///
4506    /// let result: Hatch<()> = Err(Yoshi::new(YoshiKind::Internal {
4507    ///     message: "operation failed".into(),
4508    ///     source: None,
4509    ///     component: None,
4510    /// }))
4511    /// .lay("During user authentication");
4512    ///
4513    /// assert!(result.is_err());
4514    /// let error = result.unwrap_err();
4515    /// assert!(error.to_string().contains("During user authentication"));
4516    /// ```
4517    ///
4518    /// # Errors
4519    ///
4520    /// Returns the enriched `Hatch<T>` error if `self` is `Err`, or the original
4521    /// success value if `self` is `Ok`. This method never introduces new errors.
4522    fn lay(self, message: impl Into<String>) -> Hatch<T>;
4523}
4524
4525impl<T> LayContext<T> for Hatch<T> {
4526    #[track_caller]
4527    fn lay(self, message: impl Into<String>) -> Hatch<T> {
4528        self.map_err(|e| e.lay(message))
4529    }
4530}
4531
4532/// Extension trait for mapping other `Result<T, E>` types into `Hatch<T>` easily.
4533///
4534/// This trait enables seamless integration between the Yoshi error ecosystem and
4535/// external error types. It provides the `.hatch()` method that converts any
4536/// `Result` with an error type that can be converted to `Yoshi` into a `Hatch<T>`.
4537///
4538/// # Type Requirements
4539///
4540/// The error type `E` must implement `Into<Yoshi>` to enable conversion. This is
4541/// automatically satisfied for:
4542/// - `std::io::Error` (when std feature is enabled)
4543/// - `NoStdIo` (when std feature is disabled)
4544/// - `String` and `&str` types
4545/// - Any type that implements `std::error::Error + Send + Sync + 'static`
4546///
4547/// # Performance Characteristics
4548///
4549/// - **Conversion Cost**: O(1) for types with direct `Into<Yoshi>` implementations
4550/// - **Memory Overhead**: Minimal - reuses existing error allocation where possible
4551/// - **Type Safety**: Compile-time guarantees with no runtime type checking
4552///
4553/// # Examples
4554///
4555/// ```rust
4556/// use yoshi_std::{Hatch, Hatchable, LayContext};
4557/// # use std::io;
4558///
4559/// fn file_operation() -> Hatch<String> {
4560///     std::fs::read_to_string("config.toml")
4561///         .hatch()  // Convert io::Error to Yoshi
4562///         .lay("While reading configuration file")
4563/// }
4564///
4565/// fn parse_operation() -> Hatch<i32> {
4566///     "not_a_number".parse::<i32>()
4567///         .map_err(|e| e.to_string())  // Convert to String first
4568///         .hatch()  // Then convert to Yoshi
4569///         .lay("While parsing user input")
4570/// }
4571/// ```
4572pub trait Hatchable<T, E> {
4573    /// Converts an error into a `Hatch<T>` by mapping it into `Yoshi`.
4574    ///
4575    /// This method provides a convenient way to bring external error types into
4576    /// the Yoshi ecosystem while maintaining type safety and performance efficiency.
4577    /// The conversion leverages existing `Into<Yoshi>` implementations to minimize
4578    /// overhead and maintain semantic meaning.
4579    ///
4580    /// # Type Conversion Chain
4581    ///
4582    /// The method works by applying the following transformation:
4583    /// `Result<T, E>` → `Result<T, Yoshi>` (via `E: Into<Yoshi>`)
4584    ///
4585    /// # Returns
4586    ///
4587    /// A `Hatch<T>` containing either the original success value or the converted error.
4588    ///
4589    /// # Performance Considerations
4590    ///
4591    /// - **Zero-cost for compatible types**: When `E` already has efficient `Into<Yoshi>`
4592    /// - **Minimal allocation**: Reuses existing error data structures where possible
4593    /// - **Compile-time optimization**: Fully optimizable conversion chains
4594    ///
4595    /// # Examples
4596    ///
4597    /// ```rust
4598    /// use yoshi_std::{Hatch, Hatchable};
4599    /// # use std::io;
4600    ///
4601    /// // I/O error conversion
4602    /// let io_result: Result<String, io::Error> = Err(io::Error::new(
4603    ///     io::ErrorKind::NotFound, "file not found"
4604    /// ));
4605    /// let hatched: Hatch<String> = io_result.hatch();
4606    /// assert!(hatched.is_err());
4607    ///
4608    /// // String error conversion
4609    /// let string_result: Result<i32, String> = Err("parsing failed".to_string());
4610    /// let hatched: Hatch<i32> = string_result.hatch();
4611    /// assert!(hatched.is_err());
4612    /// ```
4613    ///
4614    /// # Errors
4615    ///
4616    /// Returns a `Hatch<T>` containing the converted error if `self` is `Err`,
4617    /// or the original success value if `self` is `Ok`. Conversion errors are
4618    /// not possible as the `Into<Yoshi>` bound guarantees valid transformation.
4619    fn hatch(self) -> Hatch<T>;
4620}
4621
4622impl<T, E: Into<Yoshi>> Hatchable<T, E> for Result<T, E> {
4623    #[track_caller]
4624    fn hatch(self) -> Hatch<T> {
4625        self.map_err(Into::into)
4626    }
4627}
4628
4629//--------------------------------------------------------------------------------------------------
4630// Enhanced backtrace capture with performance monitoring
4631//--------------------------------------------------------------------------------------------------
4632
4633/// Conditionally captures a `YoshiBacktrace` based on environment variables.
4634///
4635/// This private helper function checks the `RUST_LIB_BACKTRACE` and `RUST_BACKTRACE`
4636/// environment variables. If either is set to "1" or "full", a [`YoshiBacktrace`]
4637/// is captured and returned. Otherwise, it returns `None`.
4638/// This ensures backtraces are only generated when explicitly requested,
4639/// minimizing performance overhead in production.
4640///
4641/// # Returns
4642///
4643/// An `Option` containing a [`YoshiBacktrace`] if backtrace capture is enabled,
4644/// or `None` otherwise.
4645///
4646/// # Panics
4647///
4648/// This function will panic if `OnceLock::get_or_init` is called in a `no_std` context
4649/// as its placeholder implementation panics. However, this function itself is
4650/// `#[cfg(feature = "std")]`, so it won't be compiled in `no_std`.
4651#[cfg(feature = "std")]
4652fn capture_bt() -> Option<YoshiBacktrace> {
4653    // For more robust behavior, especially in testing environments,
4654    // check the environment variables directly each time instead of caching
4655    let should =
4656        match std::env::var("RUST_LIB_BACKTRACE").or_else(|_| std::env::var("RUST_BACKTRACE")) {
4657            Ok(v) => v == "1" || v == "full", // Only enable backtrace for specific values
4658            Err(_) => false,
4659        };
4660
4661    if should {
4662        Some(YoshiBacktrace::new_captured())
4663    } else {
4664        None
4665    }
4666}
4667
4668/// Enhanced memory management utilities
4669pub mod memory {
4670    use super::{error_instance_count, intern_string, Arc, String, STRING_INTERN_POOL};
4671    /// Memory usage statistics for error handling
4672    #[derive(Debug, Default)]
4673    pub struct MemoryStats {
4674        /// Total number of Yoshi error instances created since application start
4675        pub total_errors_created: u32,
4676        /// Total number of context objects created across all errors
4677        pub total_contexts_created: u64,
4678        /// Number of string interning cache hits for memory optimization
4679        pub string_intern_hits: usize,
4680        /// Number of string interning cache misses requiring new allocations
4681        pub string_intern_misses: usize,
4682        /// Estimated bytes saved through string interning and optimization
4683        pub estimated_memory_saved: usize,
4684    }
4685
4686    /// Get comprehensive memory usage statistics
4687    pub fn get_memory_stats() -> MemoryStats {
4688        let (hits, misses) = STRING_INTERN_POOL
4689            .get()
4690            .map_or((0, 0), super::StringInternPool::stats);
4691
4692        MemoryStats {
4693            total_errors_created: error_instance_count(),
4694            total_contexts_created: 0, // Would need tracking
4695            string_intern_hits: hits,
4696            string_intern_misses: misses,
4697            estimated_memory_saved: hits * 32, // Rough estimate
4698        }
4699    }
4700
4701    /// Memory-efficient string creation with automatic interning
4702    pub fn efficient_string(s: impl Into<String>) -> Arc<str> {
4703        intern_string(s)
4704    }
4705
4706    /// Triggers cleanup of the string interning pool for long-running applications
4707    #[cfg(feature = "std")]
4708    pub fn cleanup_intern_pool() {
4709        if let Some(pool) = STRING_INTERN_POOL.get() {
4710            pool.clear_pool();
4711        }
4712    }
4713}
4714
4715//--------------------------------------------------------------------------------------------------
4716// Advanced async error handling module with Rust 1.87 enhancements
4717//--------------------------------------------------------------------------------------------------
4718
4719#[cfg(feature = "std")]
4720pub mod async_error_handling {
4721    //! Advanced async error processing utilities with precise capturing and performance optimization.
4722
4723    use super::{Result, String, Vec, Yoshi, YoshiKind};
4724    use std::future::Future;
4725    use std::time::Duration;
4726
4727    #[cfg(feature = "async")]
4728    #[allow(unused_imports)]
4729    use tokio::time;
4730
4731    /// Async error propagation with enhanced context preservation
4732    ///
4733    /// # Errors
4734    ///
4735    /// Returns a `Yoshi` error if the future resolves to an error, with additional context added.
4736    pub async fn propagate_async<T, E>(
4737        future: impl Future<Output = Result<T, E>>,
4738        context: impl Into<String>,
4739    ) -> Result<T, Yoshi>
4740    where
4741        E: Into<Yoshi>,
4742    {
4743        match future.await {
4744            Ok(value) => Ok(value),
4745            Err(error) => Err(error.into().context(context.into())),
4746        }
4747    }
4748
4749    /// Async error recovery with exponential backoff
4750    ///
4751    /// # Errors
4752    ///
4753    /// Returns a `Yoshi` error if all retry attempts fail or if the error is not transient.
4754    pub async fn retry_with_backoff<T, F, Fut>(
4755        mut operation: F,
4756        max_retries: usize,
4757        base_delay: Duration,
4758    ) -> Result<T, Yoshi>
4759    where
4760        F: FnMut() -> Fut,
4761        Fut: Future<Output = Result<T, Yoshi>>,
4762    {
4763        let mut delay = base_delay;
4764
4765        for attempt in 0..=max_retries {
4766            match operation().await {
4767                Ok(result) => return Ok(result),
4768                Err(error) if attempt == max_retries => return Err(error),
4769                Err(error) if error.is_transient() => {
4770                    // Use async sleep for proper async compatibility
4771                    #[cfg(feature = "async")]
4772                    tokio::time::sleep(delay).await;
4773                    #[cfg(not(feature = "async"))]
4774                    std::thread::sleep(delay);
4775                    delay *= 2;
4776                }
4777                Err(error) => return Err(error),
4778            }
4779        }
4780
4781        unreachable!()
4782    }
4783
4784    /// Async error aggregation for parallel operations
4785    ///
4786    /// # Errors
4787    ///
4788    /// Returns a `Yoshi` error with multiple errors aggregated if any operations fail.
4789    pub async fn aggregate_errors<I, F, Fut, T>(operations: I) -> Result<Vec<T>, Yoshi>
4790    where
4791        I: IntoIterator<Item = F>,
4792        F: FnOnce() -> Fut,
4793        Fut: Future<Output = Result<T, Yoshi>>,
4794    {
4795        let futures: Vec<_> = operations.into_iter().map(|op| op()).collect();
4796        // Simple join_all implementation without futures dependency
4797        let mut results = Vec::new();
4798        for fut in futures {
4799            results.push(fut.await);
4800        }
4801
4802        let mut successes = Vec::new();
4803        let mut errors = Vec::new();
4804
4805        for result in results {
4806            match result {
4807                Ok(value) => successes.push(value),
4808                Err(error) => errors.push(error),
4809            }
4810        }
4811
4812        if errors.is_empty() {
4813            Ok(successes)
4814        } else {
4815            Err(Yoshi::new(YoshiKind::Multiple {
4816                errors,
4817                primary_index: Some(0),
4818            }))
4819        }
4820    }
4821}
4822
4823//--------------------------------------------------------------------------------------------------
4824// Cross-process communication and error reporting
4825//--------------------------------------------------------------------------------------------------
4826
4827#[cfg(all(feature = "std", feature = "serde"))]
4828pub mod process_communication {
4829    //! Cross-process error reporting and coordination with enterprise-grade reliability.
4830
4831    use super::{Arc, HashMap, OnceLock, Result, String, SystemTime, ToString, Yoshi};
4832    use serde::{self, Deserializer, Serializer};
4833    use serde_json;
4834    use std::sync::mpsc;
4835    use std::thread;
4836
4837    // Helper functions for SystemTime serialization/deserialization for std
4838    // (Serializes as seconds since UNIX_EPOCH)
4839    mod serde_system_time {
4840        use super::{Deserializer, Serializer};
4841        use serde::Deserialize;
4842        use std::time::{SystemTime, UNIX_EPOCH};
4843        #[allow(clippy::trivially_copy_pass_by_ref)]
4844        pub fn serialize<S>(time: &SystemTime, serializer: S) -> Result<S::Ok, S::Error>
4845        where
4846            S: Serializer,
4847        {
4848            let duration = time
4849                .duration_since(UNIX_EPOCH)
4850                .map_err(serde::ser::Error::custom)?;
4851            serializer.serialize_u64(duration.as_secs())
4852        }
4853
4854        pub fn deserialize<'de, D>(deserializer: D) -> Result<SystemTime, D::Error>
4855        where
4856            D: Deserializer<'de>,
4857        {
4858            let secs = u64::deserialize(deserializer)?;
4859            Ok(UNIX_EPOCH + std::time::Duration::from_secs(secs))
4860        }
4861    }
4862
4863    /// Cross-process error reporter with structured logging
4864    pub struct ProcessErrorReporter {
4865        sender: mpsc::Sender<ProcessError>,
4866        _handle: thread::JoinHandle<()>,
4867    }
4868    /// Serializable error for cross-process communication
4869    #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
4870    pub struct ProcessError {
4871        /// Unique identifier for the process that generated this error
4872        pub process_id: u32,
4873        /// String identifier for the thread within the process
4874        pub thread_id: String,
4875        /// Human-readable error message describing the failure
4876        pub error_message: String,
4877        /// Classification of the error type for categorization
4878        pub error_kind: String,
4879        /// Severity level from 0 (info) to 255 (critical)
4880        pub severity: u8,
4881        /// System timestamp when the error occurred
4882        #[serde(with = "serde_system_time")]
4883        pub timestamp: SystemTime,
4884        /// Additional metadata for enhanced error context
4885        #[serde(
4886            serialize_with = "super::serde_helpers::serialize_arc_str_map",
4887            deserialize_with = "super::serde_helpers::deserialize_arc_str_map"
4888        )]
4889        pub metadata: HashMap<Arc<str>, Arc<str>>,
4890    }
4891
4892    impl Default for ProcessErrorReporter {
4893        fn default() -> Self {
4894            Self::new()
4895        }
4896    }
4897
4898    impl ProcessErrorReporter {
4899        /// Creates a new process error reporter with background processing
4900        #[must_use]
4901        pub fn new() -> Self {
4902            let (sender, receiver) = mpsc::channel::<ProcessError>();
4903
4904            let handle = thread::spawn(move || {
4905                while let Ok(error) = receiver.recv() {
4906                    // Process and log the error
4907                    eprintln!(
4908                        "[PROCESS-ERROR] {}: {} (PID: {}, Severity: {})",
4909                        error.timestamp.elapsed().map(|d| d.as_secs()).unwrap_or(0),
4910                        error.error_message,
4911                        error.process_id,
4912                        error.severity
4913                    ); // Write to structured log file (using serde_json for robust serialization)
4914                    if let Ok(json_log) = serde_json::to_string(&error) {
4915                        println!("STRUCTURED_LOG: {json_log}");
4916                    } else {
4917                        eprintln!("Failed to serialize process error to JSON.");
4918                    }
4919                }
4920            });
4921
4922            Self {
4923                sender,
4924                _handle: handle,
4925            }
4926        }
4927        /// Reports an error to the cross-process system
4928        ///
4929        /// # Errors
4930        ///
4931        /// Returns `mpsc::SendError<ProcessError>` if the cross-process communication
4932        /// channel is disconnected or the receiver has been dropped.
4933        pub fn report_error(&self, error: &Yoshi) -> Result<(), mpsc::SendError<ProcessError>> {
4934            let process_error = ProcessError {
4935                process_id: std::process::id(),
4936                thread_id: format!("{:?}", std::thread::current().id()),
4937                error_message: error.to_string(),
4938                error_kind: format!("{:?}", error.kind()),
4939                severity: error.severity(),
4940                timestamp: SystemTime::now(),
4941                metadata: error
4942                    .primary_context()
4943                    .map(|ctx| ctx.metadata.clone())
4944                    .unwrap_or_default(),
4945            };
4946
4947            self.sender.send(process_error)
4948        }
4949    }
4950
4951    /// Global process error coordinator
4952    static PROCESS_REPORTER: OnceLock<ProcessErrorReporter> = OnceLock::new();
4953
4954    /// Gets or initializes the global process error reporter
4955    pub fn global_reporter() -> &'static ProcessErrorReporter {
4956        PROCESS_REPORTER.get_or_init(ProcessErrorReporter::new)
4957    }
4958
4959    /// Reports an error to the global cross-process system
4960    pub fn report_global_error(error: &Yoshi) {
4961        if let Err(e) = global_reporter().report_error(error) {
4962            eprintln!("Failed to report error to cross-process system: {e}");
4963        }
4964    }
4965}
4966
4967//--------------------------------------------------------------------------------------------------
4968// SIMD-optimized string processing for high-performance formatting
4969//--------------------------------------------------------------------------------------------------
4970
4971#[cfg(all(feature = "unstable-metrics", target_arch = "x86_64"))]
4972pub mod simd_optimization {
4973    //! SIMD-accelerated string processing for optimal error formatting performance.
4974
4975    use super::{String, ToString, Vec, Yoshi};
4976
4977    /// SIMD-optimized string formatting buffer
4978    pub struct SimdFormatBuffer {
4979        data: Vec<u8>,
4980        capacity: usize,
4981    }
4982
4983    impl SimdFormatBuffer {
4984        /// Creates a new SIMD-optimized format buffer
4985        #[must_use]
4986        pub fn new() -> Self {
4987            Self::with_capacity(4096)
4988        }
4989
4990        /// Creates a buffer with specified capacity aligned for SIMD operations
4991        #[must_use]
4992        pub fn with_capacity(capacity: usize) -> Self {
4993            // Align capacity to 32-byte boundaries for AVX2 operations
4994            let aligned_capacity = (capacity + 31) & !31;
4995            Self {
4996                data: Vec::with_capacity(aligned_capacity),
4997                capacity: aligned_capacity,
4998            }
4999        }
5000
5001        /// SIMD-accelerated string concatenation
5002        pub fn append_simd(&mut self, s: &str) {
5003            let bytes = s.as_bytes();
5004            let new_len = self.data.len() + bytes.len();
5005
5006            if new_len > self.capacity {
5007                self.grow_aligned(new_len);
5008            }
5009
5010            // Use SIMD operations for large strings
5011            if bytes.len() >= 32 {
5012                unsafe { self.append_simd_internal(bytes) };
5013            } else {
5014                self.data.extend_from_slice(bytes);
5015            }
5016        }
5017
5018        /// Internal SIMD implementation using safe intrinsics
5019        #[target_feature(enable = "avx2")]
5020        unsafe fn append_simd_internal(&mut self, bytes: &[u8]) {
5021            #[cfg(target_arch = "x86_64")]
5022            {
5023                use std::arch::x86_64::{_mm256_loadu_si256, _mm256_storeu_si256};
5024
5025                let chunks = bytes.chunks_exact(32);
5026                let remainder = chunks.remainder();
5027                for chunk in chunks {
5028                    let simd_data = _mm256_loadu_si256(chunk.as_ptr().cast());
5029                    let dst_ptr = self.data.as_mut_ptr().add(self.data.len()).cast();
5030                    _mm256_storeu_si256(dst_ptr, simd_data);
5031                    self.data.set_len(self.data.len() + 32);
5032                }
5033
5034                // Handle remaining bytes
5035                if !remainder.is_empty() {
5036                    self.data.extend_from_slice(remainder);
5037                }
5038            }
5039        }
5040
5041        /// Grows the buffer with proper alignment
5042        fn grow_aligned(&mut self, min_capacity: usize) {
5043            let new_capacity = ((min_capacity * 2) + 31) & !31;
5044            self.data.reserve_exact(new_capacity - self.data.capacity());
5045            self.capacity = new_capacity;
5046        }
5047        /// Returns the formatted string
5048        #[must_use]
5049        pub fn as_str(&self) -> &str {
5050            // SAFETY: We only append valid UTF-8 strings
5051            unsafe { std::str::from_utf8_unchecked(&self.data) }
5052        }
5053
5054        /// Clears the buffer while preserving capacity
5055        pub fn clear(&mut self) {
5056            self.data.clear();
5057        }
5058    }
5059
5060    impl Default for SimdFormatBuffer {
5061        fn default() -> Self {
5062            Self::new()
5063        }
5064    }
5065
5066    /// SIMD-optimized error formatting
5067    pub fn format_error_simd(error: &Yoshi) -> String {
5068        let mut buffer = SimdFormatBuffer::new();
5069
5070        // Format main error
5071        buffer.append_simd(&format!("{}", error.kind()));
5072
5073        // Add contexts with SIMD acceleration
5074        for context in error.contexts() {
5075            if let Some(ref message) = context.message {
5076                buffer.append_simd("\nCaused by: ");
5077                buffer.append_simd(message);
5078            }
5079        }
5080
5081        buffer.as_str().to_string()
5082    }
5083}
5084
5085//--------------------------------------------------------------------------------------------------
5086// Cross-process metrics and telemetry
5087//--------------------------------------------------------------------------------------------------
5088
5089#[cfg(feature = "unstable-metrics")]
5090pub mod cross_process_metrics {
5091    //! Global error metrics and telemetry system with cross-process coordination.
5092
5093    use super::{OnceLock, SystemTime, Yoshi};
5094    use std::collections::HashMap;
5095    use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
5096
5097    /// Global error metrics collector
5098    #[derive(Debug)]
5099    pub struct ErrorMetrics {
5100        total_errors: AtomicU32,
5101        #[allow(dead_code)]
5102        errors_by_kind: HashMap<&'static str, AtomicU32>,
5103        errors_by_severity: [AtomicU32; 256],
5104        memory_usage: AtomicUsize,
5105        #[allow(dead_code)]
5106        processing_time: AtomicU32,
5107    }
5108    impl Default for ErrorMetrics {
5109        /// Creates a new metrics collector
5110        fn default() -> Self {
5111            Self {
5112                total_errors: AtomicU32::new(0),
5113                errors_by_kind: HashMap::new(),
5114                errors_by_severity: [const { AtomicU32::new(0) }; 256],
5115                memory_usage: AtomicUsize::new(0),
5116                processing_time: AtomicU32::new(0),
5117            }
5118        }
5119    }
5120
5121    impl ErrorMetrics {
5122        /// Creates a new metrics collector
5123        #[must_use]
5124        pub fn new() -> Self {
5125            Self::default()
5126        }
5127
5128        /// Records an error occurrence
5129        pub fn record_error(&self, error: &Yoshi) {
5130            self.total_errors.fetch_add(1, Ordering::Relaxed);
5131
5132            // Record by severity
5133            let severity = error.severity() as usize;
5134            self.errors_by_severity[severity].fetch_add(1, Ordering::Relaxed);
5135
5136            // Estimate memory usage
5137            let estimated_size = std::mem::size_of_val(error)
5138                + error
5139                    .contexts()
5140                    .map(|ctx| {
5141                        ctx.message.as_ref().map_or(0, |m| m.len()) + ctx.metadata.len() * 64
5142                        // Rough estimate
5143                    })
5144                    .sum::<usize>();
5145
5146            self.memory_usage
5147                .fetch_add(estimated_size, Ordering::Relaxed);
5148        }
5149
5150        /// Gets total error count
5151        #[must_use]
5152        pub fn total_errors(&self) -> u32 {
5153            self.total_errors.load(Ordering::Relaxed)
5154        }
5155
5156        /// Gets errors by severity level
5157        #[must_use]
5158        pub fn errors_by_severity(&self, severity: u8) -> u32 {
5159            self.errors_by_severity[severity as usize].load(Ordering::Relaxed)
5160        }
5161
5162        /// Gets estimated memory usage
5163        #[must_use]
5164        pub fn memory_usage(&self) -> usize {
5165            self.memory_usage.load(Ordering::Relaxed)
5166        }
5167
5168        /// Generates a metrics report
5169        #[must_use]
5170        pub fn generate_report(&self) -> MetricsReport {
5171            MetricsReport {
5172                total_errors: self.total_errors(),
5173                high_severity_errors: (200..=255).map(|s| self.errors_by_severity(s)).sum(),
5174                medium_severity_errors: (100..199).map(|s| self.errors_by_severity(s)).sum(),
5175                low_severity_errors: (0..99).map(|s| self.errors_by_severity(s)).sum(),
5176                memory_usage: self.memory_usage(),
5177                timestamp: SystemTime::now(),
5178            }
5179        }
5180    }
5181    /// Metrics report structure
5182    #[derive(Debug, Clone)]
5183    pub struct MetricsReport {
5184        /// Total number of errors recorded
5185        pub total_errors: u32,
5186        /// Number of high-severity errors
5187        pub high_severity_errors: u32,
5188        /// Number of medium-severity errors
5189        pub medium_severity_errors: u32,
5190        /// Number of low-severity errors
5191        pub low_severity_errors: u32,
5192        /// Current memory usage in bytes
5193        pub memory_usage: usize,
5194        /// Timestamp when the report was generated
5195        pub timestamp: SystemTime,
5196    }
5197
5198    /// Global metrics instance
5199    static GLOBAL_METRICS: OnceLock<ErrorMetrics> = OnceLock::new();
5200
5201    /// Gets the global metrics collector
5202    pub fn global_metrics() -> &'static ErrorMetrics {
5203        GLOBAL_METRICS.get_or_init(ErrorMetrics::new)
5204    }
5205
5206    /// Records an error in global metrics
5207    pub fn record_global_error(error: &Yoshi) {
5208        global_metrics().record_error(error);
5209    }
5210    /// Gets a global metrics report
5211    #[must_use]
5212    pub fn global_report() -> MetricsReport {
5213        global_metrics().generate_report()
5214    }
5215
5216    /// Resets global metrics (primarily for testing)
5217    #[cfg(test)]
5218    pub fn reset_global_metrics() {
5219        // This would require a more sophisticated reset mechanism in production
5220        // For now, we just create a new instance
5221        // Note: This doesn't actually reset the OnceLock, just documents the intention
5222    }
5223}
5224
5225//--------------------------------------------------------------------------------------------------
5226// Comprehensive test suite with performance validation
5227//--------------------------------------------------------------------------------------------------
5228
5229#[cfg(test)]
5230mod tests {
5231    use super::*;
5232    // TypeId is not needed for checking foreign error names after switching to type_name!
5233    // use core::any::TypeId; // For TypeId usage in tests
5234
5235    #[cfg(feature = "std")]
5236    use std::io::ErrorKind;
5237    #[cfg(feature = "std")]
5238    use std::{env, io};
5239
5240    #[test]
5241    fn test_error_instance_counter() {
5242        // Reset counter to ensure test isolation for precise counting
5243        reset_error_instance_counter();
5244
5245        let initial_count = error_instance_count();
5246        let _err1 = Yoshi::new(YoshiKind::Internal {
5247            message: "test".into(),
5248            source: None,
5249            component: None,
5250        });
5251        let after_first_count = error_instance_count();
5252        // Allow for some variance due to potential concurrent test execution
5253        assert!(
5254            after_first_count > initial_count,
5255            "Creating first error should increment counter by at least 1"
5256        );
5257
5258        let _err2 = Yoshi::new(YoshiKind::Internal {
5259            message: "test".into(),
5260            source: None,
5261            component: None,
5262        });
5263        let after_second_count = error_instance_count();
5264        // Creating the second error should also increment by at least 1
5265        assert!(
5266            after_second_count > after_first_count,
5267            "Creating second error should increment counter by at least 1"
5268        );
5269    }
5270
5271    #[test]
5272    fn test_yoshikind_io_display() {
5273        #[cfg(feature = "std")]
5274        {
5275            let io_err = io::Error::new(ErrorKind::NotFound, "file not found");
5276            let kind = YoshiKind::Io(io_err);
5277            assert_eq!(kind.to_string(), "I/O error: file not found");
5278        }
5279        #[cfg(not(feature = "std"))]
5280        {
5281            let kind = YoshiKind::Io(NoStdIo::GenericIo("memory exhausted".into()));
5282            assert_eq!(kind.to_string(), "I/O error (no_std): memory exhausted");
5283        }
5284    }
5285
5286    #[test]
5287    fn test_yoshikind_resource_exhausted_display() {
5288        let kind = YoshiKind::ResourceExhausted {
5289            resource: "memory".into(),
5290            limit: "1GB".into(),
5291            current: "1.2GB".into(),
5292            usage_percentage: Some(120.0),
5293        };
5294        assert_eq!(
5295            kind.to_string(),
5296            "Resource 'memory' exhausted: 1.2GB (limit: 1GB) [120.0% usage]"
5297        );
5298    }
5299
5300    #[test]
5301    fn test_yoshikind_timeout_uses_core_duration() {
5302        let kind = YoshiKind::Timeout {
5303            operation: "long_task".into(),
5304            duration: Duration::from_secs(5),
5305            expected_max: None,
5306        };
5307        assert_eq!(kind.to_string(), "Operation 'long_task' timed out after 5s");
5308        // Verify type is core::time::Duration
5309        let _duration: Duration = match kind {
5310            YoshiKind::Timeout { duration, .. } => duration,
5311            _ => panic!("Expected Timeout variant"),
5312        };
5313    }
5314
5315    #[test]
5316    fn test_from_std_io_error() {
5317        #[cfg(feature = "std")]
5318        {
5319            let io_err = io::Error::new(ErrorKind::NotFound, "file not found");
5320            let yoshi_err = Yoshi::from(io_err);
5321            assert!(format!("{yoshi_err}").contains("I/O error: file not found"));
5322            assert!(matches!(yoshi_err.kind, YoshiKind::Io(_)));
5323        }
5324        #[cfg(not(feature = "std"))]
5325        {
5326            let no_std_io_err = NoStdIo::new("no_std file not found");
5327            let yoshi_err = Yoshi::from(no_std_io_err);
5328            assert!(format!("{yoshi_err}").contains("I/O error (no_std): no_std file not found"));
5329            assert!(matches!(yoshi_err.kind, YoshiKind::Io(_)));
5330        }
5331    }
5332
5333    #[test]
5334    fn test_from_string() {
5335        let msg = "simple string error".to_string();
5336        let yoshi_err = Yoshi::from(msg.clone());
5337        assert!(matches!(
5338            yoshi_err.kind,
5339            YoshiKind::Internal {
5340                ref message, ..
5341            } if message.as_ref() == msg
5342        ));
5343        assert!(format!("{yoshi_err}").contains(&msg));
5344    }
5345
5346    #[test]
5347    fn test_yoshi_foreign_from_boxed_error() {
5348        #[derive(Debug)]
5349        struct MyCustomError;
5350        impl Display for MyCustomError {
5351            fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
5352                write!(f, "a custom error occurred")
5353            }
5354        }
5355        impl Error for MyCustomError {}
5356
5357        let boxed_err = Box::new(MyCustomError);
5358        let yoshi_err = Yoshi::foreign(boxed_err); // Changed to Yoshi::foreign
5359        assert!(format!("{yoshi_err}").contains("a custom error occurred"));
5360        assert!(matches!(yoshi_err.kind, YoshiKind::Foreign { .. }));
5361        if let YoshiKind::Foreign {
5362            error_type_name, ..
5363        } = yoshi_err.kind
5364        {
5365            assert_eq!(error_type_name.as_ref(), "alloc::boxed::Box<yoshi_std::tests::test_yoshi_foreign_from_boxed_error::MyCustomError>");
5366        } else {
5367            panic!("Expected Foreign kind");
5368        }
5369    }
5370
5371    #[test]
5372    fn test_contextualization() {
5373        #[cfg(feature = "std")]
5374        let base_err = io::Error::new(ErrorKind::PermissionDenied, "access denied");
5375        #[cfg(not(feature = "std"))]
5376        let base_err = NoStdIo::new("access denied");
5377
5378        let yoshi_err = Yoshi::from(base_err)
5379            .context("Attempted to write to a protected directory".to_string())
5380            .with_metadata("user_id".to_string(), "guest".to_string())
5381            .with_suggestion("Try running with elevated privileges".to_string())
5382            .with_priority(200);
5383
5384        let err_string = format!("{yoshi_err}");
5385        assert!(err_string.contains("access denied"));
5386        assert!(err_string.contains("Caused by: Attempted to write to a protected directory"));
5387        assert!(err_string.contains("user_id: guest"));
5388        assert!(err_string.contains("Suggestion: Try running with elevated privileges"));
5389        assert_eq!(yoshi_err.primary_context().unwrap().priority, 200);
5390    }
5391    #[test]
5392    fn test_chained_yoshi_kind() {
5393        let inner_yoshi = Yoshi::new(YoshiKind::Network {
5394            message: "Connection refused".into(),
5395            source: None,
5396            error_code: None,
5397        });
5398
5399        let outer_yoshi = Yoshi::new(YoshiKind::Internal {
5400            message: "Service communication failed".into(),
5401            source: Some(Box::new(inner_yoshi)),
5402            component: None,
5403        });
5404        let err_string = format!("{outer_yoshi}");
5405
5406        assert!(err_string.contains("Internal error: Service communication failed"));
5407        assert!(err_string.contains("Caused by: Network error: Connection refused")); // Check for nested display
5408        assert!(!err_string.contains("Original Cause: Network error: Connection refused"));
5409        // Should not be duplicated
5410    }
5411
5412    #[test]
5413    #[cfg(feature = "std")]
5414    fn test_backtrace_capture_if_enabled() {
5415        let original_rust_backtrace = env::var("RUST_BACKTRACE").ok();
5416        let original_production_mode = env::var("YOSHI_PRODUCTION_MODE").ok();
5417
5418        // Ensure we're not in production mode which would sanitize the backtrace
5419        env::remove_var("YOSHI_PRODUCTION_MODE");
5420        env::set_var("RUST_BACKTRACE", "1");
5421
5422        let err = Yoshi::new(YoshiKind::Internal {
5423            message: "Test internal error with backtrace".into(),
5424            source: None,
5425            component: None,
5426        });
5427        assert!(err.backtrace().is_some());
5428
5429        let formatted_error = format!("{err}");
5430        // Check for the backtrace framework indicator that's always included
5431        assert!(formatted_error.contains("Generated by std::backtrace framework"));
5432        assert!(formatted_error.contains("Backtrace captured at:"));
5433
5434        // Restore original environment
5435        if let Some(val) = original_rust_backtrace {
5436            env::set_var("RUST_BACKTRACE", val);
5437        } else {
5438            env::remove_var("RUST_BACKTRACE");
5439        }
5440
5441        if let Some(val) = original_production_mode {
5442            env::set_var("YOSHI_PRODUCTION_MODE", val);
5443        }
5444    }
5445
5446    #[test]
5447    fn test_no_backtrace_if_disabled() {
5448        #[cfg(feature = "std")]
5449        let original_rust_backtrace = env::var("RUST_BACKTRACE").ok();
5450        #[cfg(feature = "std")]
5451        env::remove_var("RUST_BACKTRACE");
5452
5453        let err = Yoshi::new(YoshiKind::Internal {
5454            message: "No backtrace expected".into(),
5455            source: None,
5456            component: None,
5457        });
5458
5459        #[cfg(feature = "std")]
5460        assert!(err.backtrace().is_none());
5461        #[cfg(not(feature = "std"))]
5462        assert!(err.backtrace.is_none());
5463
5464        assert!(!format!("{err}").contains("stack backtrace"));
5465
5466        #[cfg(feature = "std")]
5467        {
5468            if let Some(val) = original_rust_backtrace {
5469                env::set_var("RUST_BACKTRACE", val);
5470            }
5471        }
5472    }
5473
5474    #[test]
5475    fn test_access_metadata_directly() {
5476        let err = Yoshi::new(YoshiKind::Internal {
5477            message: "Test provide metadata".into(),
5478            source: None,
5479            component: None,
5480        })
5481        .with_metadata("id", "123")
5482        .with_metadata("status", "failed");
5483
5484        // Access metadata directly from the YoContext
5485        let ctx = err
5486            .primary_context()
5487            .expect("Should have a primary context");
5488        let map = &ctx.metadata;
5489        assert_eq!(map.get(&Arc::from("id")), Some(&Arc::from("123")));
5490        assert_eq!(map.get(&Arc::from("status")), Some(&Arc::from("failed")));
5491    }
5492
5493    #[test]
5494    fn test_yoshi_location_macro() {
5495        let loc = yoshi_location!();
5496        assert!(loc.file.ends_with("lib.rs"));
5497        assert!(loc.line > 0);
5498        assert!(loc.column > 0);
5499        assert_eq!(
5500            format!("{loc}"),
5501            format!("{}:{}:{}", loc.filename(), loc.line, loc.column)
5502        );
5503    }
5504
5505    #[test]
5506    fn test_yoshi_with_payload_and_access() {
5507        #[derive(Debug, PartialEq)]
5508        struct CustomErrorPayload {
5509            code: u16,
5510            message: String,
5511        }
5512        impl Display for CustomErrorPayload {
5513            fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
5514                write!(f, "CustomPayload: code={}, msg={}", self.code, self.message)
5515            }
5516        }
5517
5518        let err = Yoshi::new(YoshiKind::Internal {
5519            message: "Operation failed with custom shell".into(),
5520            source: None,
5521            component: None,
5522        })
5523        .with_shell(CustomErrorPayload {
5524            code: 500,
5525            message: "Internal server error details".into(),
5526        })
5527        .with_shell("a string shell".to_string())
5528        .with_shell(42u32);
5529
5530        // Access payloads using the more robust `Yoshi::shell` method
5531        let ctx = err
5532            .primary_context()
5533            .expect("Should have a primary context");
5534
5535        let custom_payload = ctx.shell::<CustomErrorPayload>();
5536        assert!(custom_payload.is_some());
5537        assert_eq!(custom_payload.unwrap().code, 500);
5538
5539        let string_payload = ctx.shell::<String>();
5540        assert!(string_payload.is_some());
5541        assert_eq!(string_payload.unwrap(), &"a string shell".to_string());
5542
5543        let u32_payload = ctx.shell::<u32>();
5544        assert!(u32_payload.is_some());
5545        assert_eq!(*u32_payload.unwrap(), 42);
5546    }
5547
5548    #[test]
5549    fn test_yoshi_context_ext_with_payload_on_result() {
5550        #[derive(Debug, PartialEq)]
5551        struct TransactionId(String);
5552
5553        #[cfg(feature = "std")]
5554        let result: std::result::Result<u32, std::io::Error> = Err(io::Error::new(
5555            ErrorKind::PermissionDenied,
5556            "db write failed",
5557        ));
5558        #[cfg(not(feature = "std"))]
5559        let result: core::result::Result<u32, NoStdIo> = Err(NoStdIo::new("db write failed"));
5560
5561        let yoshi_result = result
5562            .with_shell(TransactionId("tx123".into()))
5563            .context("Failed to commit transaction".to_string());
5564
5565        assert!(yoshi_result.is_err());
5566        let err = yoshi_result.unwrap_err();
5567
5568        assert!(format!("{err}").contains("db write failed"));
5569        assert!(format!("{err}").contains("Caused by: Failed to commit transaction")); // Access shell using the corrected `Yoshi::shell` method that searches all contexts
5570        let transaction_id = err.shell::<TransactionId>();
5571
5572        assert!(transaction_id.is_some(), "Should find TransactionId shell");
5573        assert_eq!(transaction_id.unwrap().0, "tx123".to_string());
5574    }
5575
5576    #[test]
5577    fn test_yoshi_context_ext_short_aliases() {
5578        #[cfg(feature = "std")]
5579        let result: std::result::Result<(), std::io::Error> = Err(io::Error::new(
5580            io::ErrorKind::NotFound,
5581            "file.txt not found",
5582        ));
5583        #[cfg(not(feature = "std"))]
5584        let result: core::result::Result<(), NoStdIo> = Err(NoStdIo::NotFound);
5585
5586        let err = result
5587            .ctx("Failed to open file".to_string())
5588            .help("Check file path and permissions".to_string())
5589            .meta("file_name".to_string(), "file.txt".to_string())
5590            .unwrap_err();
5591
5592        let s = format!("{err}");
5593        assert!(s.contains("Failed to open file"));
5594        assert!(s.contains("Check file path and permissions"));
5595        assert!(s.contains("file_name: file.txt"));
5596    }
5597
5598    #[test]
5599    fn test_hatch_type_alias() {
5600        let success: Hatch<u32> = Ok(42);
5601        if let Ok(value) = success {
5602            assert_eq!(value, 42);
5603        } else {
5604            panic!("Expected Ok value");
5605        }
5606
5607        let failure: Hatch<u32> = Err(Yoshi::new(YoshiKind::Internal {
5608            message: "test error".into(),
5609            source: None,
5610            component: None,
5611        }));
5612        assert!(failure.is_err());
5613    }
5614
5615    #[test]
5616    fn test_lay_context_trait() {
5617        let error = Yoshi::new(YoshiKind::Internal {
5618            message: "base error".into(),
5619            source: None,
5620            component: None,
5621        });
5622
5623        let result: Hatch<()> = Err(error).lay("additional context");
5624        assert!(result.is_err());
5625
5626        let err = result.unwrap_err();
5627        assert!(err.to_string().contains("additional context"));
5628    }
5629
5630    #[test]
5631    fn test_hatchable_trait() {
5632        #[cfg(feature = "std")]
5633        {
5634            use std::io;
5635            let io_result: Result<String, io::Error> =
5636                Err(io::Error::new(io::ErrorKind::NotFound, "file not found"));
5637            let hatched = io_result.hatch();
5638            assert!(hatched.is_err());
5639        }
5640
5641        let string_result: Result<i32, String> = Err("conversion failed".to_string());
5642        let hatched = string_result.hatch();
5643        assert!(hatched.is_err());
5644    }
5645
5646    #[test]
5647    fn test_yoshi_enhanced_methods() {
5648        let error = Yoshi::new(YoshiKind::Internal {
5649            message: "base error".into(),
5650            source: None,
5651            component: None,
5652        })
5653        .lay("operation context");
5654
5655        // Test laytext method
5656        assert_eq!(error.laytext().unwrap(), "operation context");
5657
5658        // Test nest method (should be None for this error)
5659        assert!(error.nest().is_none());
5660    }
5661
5662    #[test]
5663    fn test_yum_macro() {
5664        let error = Yoshi::new(YoshiKind::Internal {
5665            message: "test error for yum".into(),
5666            source: None,
5667            component: None,
5668        })
5669        .context("test context")
5670        .with_suggestion("try again");
5671
5672        // yum! macro should not panic and should return the error
5673        let returned_error = yum!(error);
5674        assert_eq!(returned_error.laytext().unwrap(), "test context");
5675        assert_eq!(returned_error.suggestion().unwrap(), "try again");
5676    }
5677
5678    #[test]
5679    fn test_hatch_backwards_compatibility() {
5680        use core::error::Error;
5681
5682        let error = Yoshi::new(YoshiKind::Internal {
5683            message: "compatibility test".into(),
5684            source: None,
5685            component: None,
5686        });
5687
5688        // Test that standard Error trait methods still work
5689        let error_ref: &dyn Error = &error;
5690        assert!(error_ref.source().is_none());
5691
5692        // Test that new methods work alongside old ones
5693        assert!(error.nest().is_none()); // New method
5694        assert!(error.laytext().is_none()); // New method (no context added)
5695    }
5696
5697    #[test]
5698    fn test_hatch_ecosystem_integration() {
5699        // Test complete workflow with all Hatch ecosystem components
5700        fn complex_operation() -> Hatch<u32> {
5701            #[cfg(feature = "std")]
5702            let io_result: Result<String, std::io::Error> = Err(std::io::Error::new(
5703                std::io::ErrorKind::PermissionDenied,
5704                "access denied",
5705            ));
5706            #[cfg(not(feature = "std"))]
5707            let io_result: Result<String, NoStdIo> = Err(NoStdIo::PermissionDenied);
5708
5709            io_result
5710                .hatch()
5711                .lay("while accessing configuration")
5712                .context("during system initialization")
5713                .map_err(|e| {
5714                    e.with_metadata("component", "config_loader")
5715                        .with_suggestion("check file permissions")
5716                })?;
5717
5718            Ok(42)
5719        }
5720
5721        let result = complex_operation();
5722        assert!(result.is_err());
5723
5724        let error = result.unwrap_err();
5725
5726        // Verify thematic methods work
5727        assert!(error.laytext().is_some());
5728        assert_eq!(error.suggestion().unwrap(), "check file permissions");
5729
5730        // Verify nest access works
5731        assert!(error.nest().is_some());
5732
5733        // Test yum! macro
5734        let debug_error = yum!(error);
5735        assert_eq!(debug_error.instance_id(), error.instance_id());
5736    }
5737}