use super::*;
use alloc::borrow::Cow;
use alloc::boxed::Box;
use alloc::string::ToString;
use alloc::vec;
use alloc::vec::Vec;
#[repr(transparent)]
#[derive(Clone)]
pub struct ErrorImpl {
inner: Box<ErrorImplInner>,
}
#[derive(Clone)]
struct ErrorImplInner {
steps: Vec<ErrorSourceStep>,
current_code: Option<&'static ErrorCodeInfo>,
}
impl ErrorImplFunctions for ErrorImpl {
type FrameIter<'a> = ErrorImplIter<'a>;
#[track_caller]
#[inline(never)]
fn new(source: ErrorOrigin, args: Option<&Arguments<'_>>) -> ErrorImpl {
ErrorImpl {
inner: Box::new(ErrorImplInner {
steps: vec![ErrorSourceStep {
static_info: source,
formatted_message: format_args(args),
location: Location::caller(),
}],
current_code: match source {
ErrorOrigin::StaticOrigin(o) => o.error_code,
ErrorOrigin::TypeOrigin(_, Some(code)) => code.error_code,
_ => None,
},
}),
}
}
#[track_caller]
#[inline(never)]
fn push_context(&mut self, source: &'static ErrorInfoImpl, args: Option<&Arguments<'_>>) {
let step = ErrorSourceStep {
static_info: ErrorOrigin::StaticOrigin(source),
formatted_message: format_args(args),
location: Location::caller(),
};
self.inner.steps.push(step);
if source.error_code.is_some() {
self.inner.current_code = source.error_code;
}
}
#[inline(always)]
fn code(&self) -> Option<&'static ErrorCodeInfo> {
self.inner.current_code
}
fn iter(&self) -> Self::FrameIter<'_> {
ErrorImplIter {
underlying: &self.inner,
idx: self.inner.steps.len(),
phase: FrameLoopPhase::Context,
}
}
}
fn format_args(args: Option<&Arguments>) -> Option<Cow<'static, str>> {
if let Some(args) = args {
if let Some(str) = args.as_str() {
Some(Cow::Borrowed(str))
} else {
Some(args.to_string().into())
}
} else {
None
}
}
#[derive(Clone)]
struct ErrorSourceStep {
static_info: ErrorOrigin,
location: &'static Location<'static>,
formatted_message: Option<Cow<'static, str>>,
}
pub struct ErrorImplIter<'a> {
underlying: &'a ErrorImplInner,
idx: usize,
phase: FrameLoopPhase,
}
#[derive(Copy, Clone, Eq, PartialEq)]
enum FrameLoopPhase {
Context,
LocationMismatchFrame,
Ended,
}
impl Iterator for ErrorImplIter<'_> {
type Item = ErrorFrameImpl;
fn next(&mut self) -> Option<Self::Item> {
while self.idx > 0 {
let frame = &self.underlying.steps[self.idx - 1];
if self.phase == FrameLoopPhase::Context {
self.phase = FrameLoopPhase::LocationMismatchFrame;
let info = match frame.static_info {
ErrorOrigin::StaticOrigin(info) => Some(info),
ErrorOrigin::TypeOrigin(_, info) => info,
};
return Some(ErrorFrameImpl {
data: match &frame.formatted_message {
None => match frame.static_info {
ErrorOrigin::StaticOrigin(origin) => {
ErrorFrameData::decode_static(Some(origin), None)
}
ErrorOrigin::TypeOrigin(ty, origin) => {
ErrorFrameData::TypeFrame(ty, origin.and_then(|x| x.error_code))
}
},
Some(Cow::Borrowed(str)) => {
ErrorFrameData::decode_static(info, Some(MessageContainer::Static(str)))
}
Some(Cow::Owned(str)) => ErrorFrameData::decode_static(
info,
Some(MessageContainer::Formatted(str.clone())),
),
},
location: Some(frame.location.into()),
});
}
if self.phase == FrameLoopPhase::LocationMismatchFrame {
self.phase = FrameLoopPhase::Ended;
let location = DecodedLocation::from(frame.location);
let origin = match &frame.static_info {
ErrorOrigin::StaticOrigin(origin) => origin.location,
ErrorOrigin::TypeOrigin(_, origin) => origin.and_then(|x| x.location),
};
if let Some(origin) = origin {
if !origin.is_same(location) {
return Some(ErrorFrameImpl {
data: ErrorFrameData::InternalContext(
InternalContextType::ErrorTypeConstructed,
),
location: Some(*origin),
});
}
}
}
self.idx -= 1;
self.phase = FrameLoopPhase::Context;
}
None
}
}