use std::fmt::{Debug, Display};
#[cfg(feature = "spantrace")]
use tracing_error::SpanTrace;
use crate::annotation::{Annotation, IntoMessage, IntoSuggestion};
use crate::backtrace::Backtrace;
use crate::context::Context;
use crate::erased::ErasedReport;
use crate::error::{Error, Whatever};
use crate::export::{ExportOptions, ExportedReport};
use crate::location::SourceLocation;
use crate::render;
use crate::value::{Field, Value};
struct Inner<E> {
error: E,
context: Context,
}
pub struct Report<E> {
inner: Box<Inner<E>>,
}
impl<E> Report<E> {
#[must_use]
pub fn error(&self) -> &E {
&self.inner.error
}
#[must_use]
pub fn error_mut(&mut self) -> &mut E {
&mut self.inner.error
}
#[must_use]
pub fn into_error(self) -> E {
self.inner.error
}
#[must_use]
pub fn context(&self) -> &Context {
&self.inner.context
}
}
impl<E: Error> Report<E> {
#[track_caller]
#[must_use]
pub fn new(error: E) -> Self {
Self {
inner: Box::new(Inner {
error,
context: Context::capture(),
}),
}
}
#[track_caller]
#[must_use]
pub fn whatever(description: impl IntoMessage) -> Self
where
E: Whatever,
{
Self::new(E::new()).message(description)
}
#[track_caller]
#[must_use]
pub fn message(mut self, message: impl IntoMessage) -> Self {
let message = message.into_message();
let location = SourceLocation::caller();
self.inner.context.annotations.push(Annotation::Message {
text: message.text,
location,
});
self
}
#[track_caller]
#[must_use]
pub fn suggestion(mut self, suggestion: impl IntoSuggestion) -> Self {
let suggestion = suggestion.into_suggestion();
let location = SourceLocation::caller();
self.inner.context.annotations.push(Annotation::Suggestion {
text: suggestion.text,
location,
});
self
}
#[must_use]
pub fn field(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
self.inner.context.fields.push(Field::public(key, value));
self
}
#[must_use]
pub fn field_display(self, key: impl Into<String>, value: impl Display) -> Self {
self.field(key, value.to_string())
}
#[must_use]
pub fn field_debug(self, key: impl Into<String>, value: impl Debug) -> Self {
self.field(key, format!("{value:?}"))
}
#[must_use]
pub fn sensitive_field(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
self.inner.context.fields.push(Field::sensitive(key, value));
self
}
#[must_use]
pub fn secret_field(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
self.inner.context.fields.push(Field::secret(key, value));
self
}
#[must_use]
pub fn with_factor(mut self, factor: impl Into<ErasedReport>) -> Self {
self.inner.context.factors.push(factor.into());
self
}
#[must_use]
pub fn with_factors<I>(mut self, factors: I) -> Self
where
I: IntoIterator,
I::Item: Into<ErasedReport>,
{
self.inner
.context
.factors
.extend(factors.into_iter().map(Into::into));
self
}
#[track_caller]
#[must_use]
pub fn escalate<F: Error>(self, error: F) -> Report<F> {
let mut escalated = Report::new(error);
escalated.inner.context.cause = Some(self.into());
escalated
}
#[must_use]
pub fn export(&self) -> ExportedReport {
self.export_with(ExportOptions::default())
}
#[must_use]
pub fn export_with(&self, options: ExportOptions) -> ExportedReport {
crate::export::build(
self.inner.error.type_name(),
self.inner.error.message().map(ToString::to_string),
self.inner.error.code(),
&self.inner.context,
options,
)
}
#[must_use]
pub fn render(&self, options: render::RenderOptions) -> String {
render::render(
self.inner.error.type_name(),
self.inner.error.message(),
self.inner.error.code(),
&self.inner.context,
options,
)
}
pub fn print(&self, options: render::RenderOptions) {
println!("{}", self.render(options.for_stdout()));
}
pub fn eprint(&self, options: render::RenderOptions) {
eprintln!("{}", self.render(options.for_stderr()));
}
pub(crate) fn from_capture(
error: E,
location: SourceLocation,
backtrace: Backtrace,
#[cfg(feature = "spantrace")] spantrace: SpanTrace,
) -> Self {
Self {
inner: Box::new(Inner {
error,
context: Context {
annotations: Vec::new(),
fields: Vec::new(),
location,
backtrace,
#[cfg(feature = "spantrace")]
spantrace,
cause: None,
factors: Vec::new(),
},
}),
}
}
}
impl<E: Error> Display for Report<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.render(render::RenderOptions::default()))
}
}
impl<E: Error> Debug for Report<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.render(render::RenderOptions::default().verbose()))
}
}