use std::{
backtrace::{Backtrace, BacktraceStatus},
borrow::Cow,
};
use crate::{attribute_path::AttributePath, tfplugin6, utils::CollectDiagnostics};
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)]
pub struct Diagnostics {
pub errors: Vec<Diagnostic>,
pub warnings: Vec<Diagnostic>,
}
impl Diagnostics {
pub fn add_error(&mut self, diag: Diagnostic) {
self.errors.push(diag)
}
pub fn add_warning(&mut self, diag: Diagnostic) {
self.warnings.push(diag)
}
pub fn error<S: Into<Cow<'static, str>>, D: Into<Cow<'static, str>>>(
&mut self,
summary: S,
detail: D,
attribute: AttributePath,
) {
self.add_error(Diagnostic::new(summary, detail, attribute))
}
pub fn root_error<S: Into<Cow<'static, str>>, D: Into<Cow<'static, str>>>(
&mut self,
summary: S,
detail: D,
) {
self.add_error(Diagnostic::root(summary, detail))
}
pub fn error_short<S: Into<Cow<'static, str>>>(
&mut self,
summary: S,
attribute: AttributePath,
) {
self.add_error(Diagnostic::short(summary, attribute))
}
pub fn root_error_short<S: Into<Cow<'static, str>>>(&mut self, summary: S) {
self.add_error(Diagnostic::root_short(summary))
}
pub fn warning<S: Into<Cow<'static, str>>, D: Into<Cow<'static, str>>>(
&mut self,
summary: S,
detail: D,
attribute: AttributePath,
) {
self.add_warning(Diagnostic::new(summary, detail, attribute))
}
pub fn root_warning<S: Into<Cow<'static, str>>, D: Into<Cow<'static, str>>>(
&mut self,
summary: S,
detail: D,
) {
self.add_warning(Diagnostic::root(summary, detail))
}
pub fn warning_short<S: Into<Cow<'static, str>>>(
&mut self,
summary: S,
attribute: AttributePath,
) {
self.add_warning(Diagnostic::short(summary, attribute))
}
pub fn root_warning_short<S: Into<Cow<'static, str>>>(&mut self, summary: S) {
self.add_warning(Diagnostic::root_short(summary))
}
pub fn add_diagnostics(&mut self, mut diags: Diagnostics) {
self.errors.append(&mut diags.errors);
self.warnings.append(&mut diags.warnings);
}
pub fn internal_error(&mut self) {
Option::<()>::None.collect_diagnostics(self);
}
pub fn function_error<S: Into<Cow<'static, str>>>(&mut self, index: i64, message: S) {
self.add_error(Diagnostic::function(index, message))
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Diagnostic {
pub summary: Cow<'static, str>,
pub detail: Cow<'static, str>,
pub attribute: AttributePath,
}
impl Diagnostic {
pub fn new<S: Into<Cow<'static, str>>, D: Into<Cow<'static, str>>>(
summary: S,
detail: D,
attribute: AttributePath,
) -> Self {
let backtrace = Backtrace::capture();
let mut detail = detail.into();
if backtrace.status() == BacktraceStatus::Captured {
if detail.is_empty() {
detail = format!("{}", backtrace).into();
} else {
detail = format!("{}\n{}", detail, backtrace).into();
}
}
Self {
summary: summary.into(),
detail,
attribute,
}
}
pub fn function<S: Into<Cow<'static, str>>>(index: i64, message: S) -> Self {
Self::new(
message,
String::default(),
AttributePath::function_argument(index),
)
}
pub fn root<S: Into<Cow<'static, str>>, D: Into<Cow<'static, str>>>(
summary: S,
detail: D,
) -> Self {
Self::new(summary, detail, Default::default())
}
pub fn short<S: Into<Cow<'static, str>>>(summary: S, attribute: AttributePath) -> Self {
Self::new(summary, String::default(), attribute)
}
pub fn root_short<S: Into<Cow<'static, str>>>(summary: S) -> Self {
Self::new(summary, String::default(), Default::default())
}
}
impl From<Diagnostics> for ::prost::alloc::vec::Vec<tfplugin6::Diagnostic> {
fn from(value: Diagnostics) -> Self {
use tfplugin6::diagnostic::Severity;
let map_cvt = |vec: Vec<Diagnostic>, severity: Severity| {
vec.into_iter().map(move |diag| tfplugin6::Diagnostic {
severity: severity.into(),
summary: diag.summary.into_owned(),
detail: diag.detail.into_owned(),
attribute: if diag.attribute.steps.is_empty() {
None
} else {
Some(diag.attribute.into())
},
})
};
map_cvt(value.errors, Severity::Error)
.chain(map_cvt(value.warnings, Severity::Warning))
.collect()
}
}