use super::{
super::{attachment::*, cause::*, error::*, implementation::*, problems::*},
implementation::*,
into::*,
};
use std::{any::*, env::*, error::*, fmt, io};
pub const AUTO_ATTACH_LOCATION: &str = "PROBLEMO_LOCATION";
pub const AUTO_ATTACH_BACKTRACE: &str = "PROBLEMO_BACKTRACE";
pub const PROBLEM_DISPLAY_SEPARATOR: &str = ": ";
#[derive(Debug, Eq, PartialEq, PartialOrd)]
pub struct Problem {
pub causes: Deque<Cause>,
}
impl Problem {
#[track_caller]
pub fn new(error: CapturedError) -> Self {
auto_attach(Self {
causes: [Cause::new(error)].into(),
})
}
pub fn into_error(self) -> ProblemAsError {
self.into()
}
pub fn via<ErrorT>(mut self, error: ErrorT) -> Self
where
ErrorT: 'static + Error + Send + Sync,
{
self.add_top(error.into());
self
}
pub fn with<AttachmentT>(mut self, attachment: AttachmentT) -> Self
where
AttachmentT: Any + Send + Sync,
{
self.attach(attachment);
self
}
pub fn with_once<AttachmentT>(mut self, attachment: AttachmentT) -> Self
where
AttachmentT: Any + Send + Sync,
{
self.attach_once(attachment);
self
}
pub fn maybe_with<AttachmentT>(mut self, attachment: Option<AttachmentT>) -> Self
where
AttachmentT: Any + Send + Sync,
{
if let Some(attachment) = attachment {
self.attach(attachment);
}
self
}
pub fn maybe_with_once<AttachmentT>(mut self, attachment: Option<AttachmentT>) -> Self
where
AttachmentT: Any + Send + Sync,
{
if let Some(attachment) = attachment {
self.attach_once(attachment);
}
self
}
#[track_caller]
pub fn with_location(mut self) -> Self {
self.attach_location_once();
self
}
pub fn with_backtrace(mut self) -> Self {
self.attach_backtrace_once();
self
}
pub fn into_problems(self) -> Problems {
self.into()
}
}
impl IntoProblem for Problem {
fn into_problem(self) -> Problem {
self
}
}
impl Causes for Problem {
fn problem(&self) -> &Problem {
self
}
fn iter_causes(&self) -> impl Iterator<Item = &Cause> {
self.into_iter()
}
}
impl Attachments for Problem {
fn attachments(&self) -> impl Iterator<Item = &CapturedAttachment> {
self.into_iter().flat_map(|cause| cause.attachments.iter())
}
}
impl AttachmentsMut for Problem {
fn attachments_mut(&mut self) -> impl Iterator<Item = &mut CapturedAttachment> {
self.into_iter()
.flat_map(|cause| cause.attachments.iter_mut())
}
fn attach<AttachmentT>(&mut self, attachment: AttachmentT)
where
AttachmentT: Any + Send + Sync,
{
if let Some(cause) = self.top_mut() {
cause.attach(attachment);
}
}
}
impl fmt::Display for Problem {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let mut iterator = self.into_iter().peekable();
while let Some(cause) = iterator.next() {
write!(formatter, "{}", cause.error)?;
if iterator.peek().is_some() {
write!(formatter, "{}", PROBLEM_DISPLAY_SEPARATOR)?;
}
}
Ok(())
}
}
impl Default for Problem {
#[track_caller]
fn default() -> Self {
auto_attach(Self {
causes: Default::default(),
})
}
}
impl<ErrorT> From<ErrorT> for Problem
where
ErrorT: 'static + Error + Send + Sync,
{
#[track_caller]
fn from(error: ErrorT) -> Self {
Self::new(error.into())
}
}
impl Into<io::Error> for Problem {
fn into(self) -> io::Error {
io::Error::new(io::ErrorKind::Other, self.into_error())
}
}
#[track_caller]
fn auto_attach(mut problem: Problem) -> Problem {
if env_var_enabled(AUTO_ATTACH_LOCATION) {
problem = problem.with_location();
}
if env_var_enabled(AUTO_ATTACH_BACKTRACE) {
problem = problem.with_backtrace();
}
problem
}
fn env_var_enabled(name: &'static str) -> bool {
var_os(name).map(|var| var != "0").unwrap_or(false)
}