#[cfg(test)]
pub(crate) mod test;
#[cfg(test)]
mod test_group_by_elem;
use std::{borrow::Cow, collections::BTreeMap, fmt, ops::Deref, vec};
use tracing::{debug, info};
use crate::json;
#[doc(hidden)]
#[macro_export]
macro_rules! into_caveat {
($kind:ident<$life:lifetime>) => {
impl<$life> $crate::IntoCaveat for $kind<$life> {
fn into_caveat<W: $crate::Warning>(
self,
warnings: $crate::warning::Set<W>,
) -> $crate::Caveat<Self, W> {
$crate::Caveat::new(self, warnings)
}
}
};
($kind:ty) => {
impl $crate::IntoCaveat for $kind {
fn into_caveat<W: $crate::Warning>(
self,
warnings: $crate::warning::Set<W>,
) -> $crate::Caveat<Self, W> {
$crate::Caveat::new(self, warnings)
}
}
impl $crate::warning::IntoCaveatDeferred for $kind {
fn into_caveat_deferred<W: $crate::Warning>(
self,
warnings: $crate::warning::SetDeferred<W>,
) -> $crate::warning::CaveatDeferred<Self, W> {
$crate::warning::CaveatDeferred::new(self, warnings)
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! into_caveat_all {
($($kind:ty),+) => {
$($crate::into_caveat!($kind);)+
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! from_warning_all {
($($source_kind:path => $target_kind:ident::$target_variant:ident),+) => {
$(
impl From<$source_kind> for $target_kind {
fn from(warning: $source_kind) -> Self {
$target_kind::$target_variant(warning)
}
}
impl From<$crate::warning::ErrorSet<$source_kind>> for $crate::warning::ErrorSet<$target_kind> {
fn from(set_a: $crate::warning::ErrorSet<$source_kind>) -> Self {
set_a.into_other()
}
}
impl From<$crate::warning::ErrorSetDeferred<$source_kind>> for $crate::warning::ErrorSetDeferred<$target_kind> {
fn from(set_a: $crate::warning::ErrorSetDeferred<$source_kind>) -> Self {
set_a.into_other()
}
}
)+
};
}
#[derive(Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct Id(Cow<'static, str>);
impl Id {
pub(crate) fn from_static(s: &'static str) -> Self {
Self(s.into())
}
pub(crate) fn from_string(s: String) -> Self {
Self(s.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Debug for Id {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl fmt::Display for Id {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
#[derive(Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct Path(String);
impl Path {
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Debug for Path {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl fmt::Display for Path {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
pub type Verdict<T, W> = Result<Caveat<T, W>, ErrorSet<W>>;
pub(crate) type VerdictDeferred<T, W> = Result<CaveatDeferred<T, W>, ErrorSetDeferred<W>>;
#[derive(Debug)]
pub(crate) struct CaveatDeferred<T, W: Warning> {
value: T,
warnings: SetDeferred<W>,
}
impl<T, W> CaveatDeferred<T, W>
where
T: IntoCaveatDeferred,
W: Warning,
{
pub(crate) fn new(value: T, warnings: SetDeferred<W>) -> Self {
Self { value, warnings }
}
}
impl<T, W> Deref for CaveatDeferred<T, W>
where
W: Warning,
{
type Target = T;
fn deref(&self) -> &T {
&self.value
}
}
impl<T, W> CaveatDeferred<T, W>
where
W: Warning,
{
pub fn into_parts(self) -> (T, SetDeferred<W>) {
let Self { value, warnings } = self;
(value, warnings)
}
}
#[derive(Debug)]
pub struct Caveat<T, W: Warning> {
value: T,
warnings: Set<W>,
}
impl<T, W> Caveat<T, W>
where
T: IntoCaveat,
W: Warning,
{
pub(crate) fn new(value: T, warnings: Set<W>) -> Self {
Self { value, warnings }
}
}
impl<T, W> Deref for Caveat<T, W>
where
W: Warning,
{
type Target = T;
fn deref(&self) -> &T {
&self.value
}
}
impl<T, W> Caveat<T, W>
where
W: Warning,
{
pub fn into_parts(self) -> (T, Set<W>) {
let Self { value, warnings } = self;
(value, warnings)
}
pub fn ignore_warnings(self) -> T {
self.value
}
pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> Caveat<U, W> {
let Self { value, warnings } = self;
Caveat {
value: op(value),
warnings,
}
}
}
pub(crate) trait GatherWarnings<T, W>
where
W: Warning,
{
type Output;
#[must_use = "If you want to ignore the value use `let _ =`"]
fn gather_warnings_into<WA>(self, warnings: &mut Set<WA>) -> Self::Output
where
W: Into<WA>,
WA: Warning;
}
impl<T, W> GatherWarnings<T, W> for Caveat<T, W>
where
W: Warning,
{
type Output = T;
fn gather_warnings_into<WA>(self, warnings: &mut Set<WA>) -> Self::Output
where
W: Into<WA>,
WA: Warning,
{
let Self {
value,
warnings: inner_warnings,
} = self;
warnings.extend(inner_warnings);
value
}
}
impl<T, W> GatherWarnings<T, W> for Option<Caveat<T, W>>
where
W: Warning,
{
type Output = Option<T>;
fn gather_warnings_into<WA>(self, warnings: &mut Set<WA>) -> Self::Output
where
W: Into<WA>,
WA: Warning,
{
match self {
Some(cv) => Some(cv.gather_warnings_into(warnings)),
None => None,
}
}
}
impl<T, W, E> GatherWarnings<T, W> for Result<Caveat<T, W>, E>
where
W: Warning,
E: std::error::Error,
{
type Output = Result<T, E>;
fn gather_warnings_into<WA>(self, warnings: &mut Set<WA>) -> Self::Output
where
W: Into<WA>,
WA: Warning,
{
match self {
Ok(cv) => Ok(cv.gather_warnings_into(warnings)),
Err(err) => Err(err),
}
}
}
impl<T, W> GatherWarnings<T, W> for Verdict<T, W>
where
W: Warning,
{
type Output = Result<T, ErrorSet<W>>;
fn gather_warnings_into<WA>(self, warnings: &mut Set<WA>) -> Self::Output
where
W: Into<WA>,
WA: Warning,
{
match self {
Ok(cv) => Ok(cv.gather_warnings_into(warnings)),
Err(err_set) => Err(err_set),
}
}
}
pub(crate) trait DeescalateError<T, W>
where
W: Warning,
{
#[must_use = "If you want to ignore the value use `let _ =`"]
fn deescalate_error_into<WA>(self, warnings: &mut Set<WA>) -> Option<T>
where
W: Into<WA>,
WA: Warning;
}
impl<T, W> DeescalateError<T, W> for Verdict<T, W>
where
W: Warning,
{
fn deescalate_error_into<WA>(self, warnings: &mut Set<WA>) -> Option<T>
where
W: Into<WA>,
WA: Warning,
{
match self {
Ok(cv) => Some(cv.gather_warnings_into(warnings)),
Err(err_set) => {
warnings.deescalate_error(err_set.into_other());
None
}
}
}
}
impl<T, W> DeescalateError<T, W> for Result<T, ErrorSet<W>>
where
W: Warning,
{
fn deescalate_error_into<WA>(self, warnings: &mut Set<WA>) -> Option<T>
where
W: Into<WA>,
WA: Warning,
{
match self {
Ok(cv) => Some(cv),
Err(err_set) => {
warnings.deescalate_error(err_set.into_other());
None
}
}
}
}
impl<T, W> GatherWarnings<T, W> for Vec<Caveat<T, W>>
where
W: Warning,
{
type Output = Vec<T>;
fn gather_warnings_into<WA>(self, warnings: &mut Set<WA>) -> Self::Output
where
W: Into<WA>,
WA: Warning,
{
self.into_iter()
.map(|cv| cv.gather_warnings_into(warnings))
.collect()
}
}
pub(crate) trait GatherDeferredWarnings<T, W>
where
W: Warning,
{
type Output;
#[must_use = "If you want to ignore the value use `let _ =`"]
fn gather_deferred_warnings_into<WA>(self, warnings: &mut SetDeferred<WA>) -> Self::Output
where
W: Into<WA>,
WA: Warning;
}
impl<T, W> GatherDeferredWarnings<T, W> for CaveatDeferred<T, W>
where
W: Warning,
{
type Output = T;
fn gather_deferred_warnings_into<WA>(self, warnings: &mut SetDeferred<WA>) -> Self::Output
where
W: Into<WA>,
WA: Warning,
{
let Self {
value,
warnings: inner_warnings,
} = self;
warnings.extend(inner_warnings);
value
}
}
impl<T, W> GatherDeferredWarnings<T, W> for Option<CaveatDeferred<T, W>>
where
W: Warning,
{
type Output = Option<T>;
fn gather_deferred_warnings_into<WA>(self, warnings: &mut SetDeferred<WA>) -> Self::Output
where
W: Into<WA>,
WA: Warning,
{
match self {
Some(cv) => Some(cv.gather_deferred_warnings_into(warnings)),
None => None,
}
}
}
impl<T, W, E> GatherDeferredWarnings<T, W> for Result<CaveatDeferred<T, W>, E>
where
W: Warning,
E: std::error::Error,
{
type Output = Result<T, E>;
fn gather_deferred_warnings_into<WA>(self, warnings: &mut SetDeferred<WA>) -> Self::Output
where
W: Into<WA>,
WA: Warning,
{
match self {
Ok(cv) => Ok(cv.gather_deferred_warnings_into(warnings)),
Err(err) => Err(err),
}
}
}
impl<T, W> GatherDeferredWarnings<T, W> for VerdictDeferred<T, W>
where
W: Warning,
{
type Output = Result<T, ErrorSetDeferred<W>>;
fn gather_deferred_warnings_into<WA>(self, warnings: &mut SetDeferred<WA>) -> Self::Output
where
W: Into<WA>,
WA: Warning,
{
match self {
Ok(cv) => Ok(cv.gather_deferred_warnings_into(warnings)),
Err(err_set) => Err(err_set),
}
}
}
impl<T, W> GatherDeferredWarnings<T, W> for Vec<CaveatDeferred<T, W>>
where
W: Warning,
{
type Output = Vec<T>;
fn gather_deferred_warnings_into<WA>(self, warnings: &mut SetDeferred<WA>) -> Self::Output
where
W: Into<WA>,
WA: Warning,
{
self.into_iter()
.map(|cv| cv.gather_deferred_warnings_into(warnings))
.collect()
}
}
pub trait IntoCaveat: Sized {
fn into_caveat<W: Warning>(self, warnings: Set<W>) -> Caveat<Self, W>;
}
pub(crate) trait IntoCaveatDeferred: Sized {
fn into_caveat_deferred<W: Warning>(self, warnings: SetDeferred<W>) -> CaveatDeferred<Self, W>;
}
macro_rules! peel {
($name:ident, $($other:ident,)*) => (into_caveat_tuple! { $($other,)* })
}
macro_rules! into_caveat_tuple {
() => ();
( $($name:ident,)+ ) => (
impl<$($name),+> $crate::IntoCaveat for ($($name,)+) {
fn into_caveat<W: $crate::Warning>(
self,
warnings: $crate::warning::Set<W>,
) -> $crate::Caveat<Self, W> {
$crate::Caveat::new(self, warnings)
}
}
impl<$($name),+> $crate::warning::IntoCaveatDeferred for ($($name,)+) {
fn into_caveat_deferred<W: $crate::Warning>(
self,
warnings: SetDeferred<W>,
) -> $crate::warning::CaveatDeferred<Self, W> {
$crate::warning::CaveatDeferred::new(self, warnings)
}
}
peel! { $($name,)+ }
)
}
into_caveat_tuple! { T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, }
into_caveat_all!(
(),
bool,
char,
u8,
u16,
u32,
u64,
u128,
i8,
i16,
i32,
i64,
i128
);
impl IntoCaveat for Cow<'_, str> {
fn into_caveat<W: Warning>(self, warnings: Set<W>) -> Caveat<Self, W> {
Caveat::new(self, warnings)
}
}
impl<T> IntoCaveat for Option<T>
where
T: IntoCaveat,
{
fn into_caveat<W: Warning>(self, warnings: Set<W>) -> Caveat<Self, W> {
Caveat::new(self, warnings)
}
}
impl<T> IntoCaveat for Vec<T>
where
T: IntoCaveat,
{
fn into_caveat<W: Warning>(self, warnings: Set<W>) -> Caveat<Self, W> {
Caveat::new(self, warnings)
}
}
impl<T> IntoCaveatDeferred for Vec<T>
where
T: IntoCaveat,
{
fn into_caveat_deferred<W: Warning>(self, warnings: SetDeferred<W>) -> CaveatDeferred<Self, W> {
CaveatDeferred::new(self, warnings)
}
}
pub trait VerdictExt<T, W: Warning> {
fn map_caveat<F, U>(self, op: F) -> Verdict<U, W>
where
F: FnOnce(T) -> U;
fn only_error(self) -> Result<Caveat<T, W>, Error<W>>;
}
#[allow(dead_code, reason = "for debugging")]
pub(crate) trait VerdictTrace<T, W: Warning> {
fn info_verdict(self, msg: &'static str) -> Self;
fn debug_verdict(self, msg: &'static str) -> Self;
}
#[allow(dead_code, reason = "for debugging")]
pub(crate) trait ResultTrace<T, W: Warning> {
fn info_result(self, msg: &'static str) -> Self;
fn debug_result(self, msg: &'static str) -> Self;
}
impl<T, W: Warning> VerdictExt<T, W> for Verdict<T, W> {
fn map_caveat<F, U>(self, op: F) -> Verdict<U, W>
where
F: FnOnce(T) -> U,
{
match self {
Ok(c) => Ok(c.map(op)),
Err(w) => Err(w),
}
}
fn only_error(self) -> Result<Caveat<T, W>, Error<W>> {
match self {
Ok(c) => Ok(c),
Err(err_set) => {
let ErrorSet { error, warnings: _ } = err_set;
Err(*error)
}
}
}
}
impl<T, W: Warning> VerdictTrace<T, W> for Verdict<T, W>
where
T: fmt::Debug,
{
fn info_verdict(self, msg: &'static str) -> Self {
match self {
Ok(c) => {
info!("{msg}: {c:#?}");
Ok(c)
}
Err(err_set) => {
info!("{msg}: {err_set:#?}");
Err(err_set)
}
}
}
fn debug_verdict(self, msg: &'static str) -> Self {
match self {
Ok(c) => {
debug!("{msg}: {c:#?}");
Ok(c)
}
Err(err_set) => {
debug!("{msg}: {err_set:#?}");
Err(err_set)
}
}
}
}
impl<T, W: Warning> ResultTrace<T, W> for Result<T, ErrorSet<W>>
where
T: fmt::Debug,
{
fn info_result(self, msg: &'static str) -> Self {
match self {
Ok(c) => {
info!("{msg}: {c:#?}");
Ok(c)
}
Err(err_set) => {
info!("{msg}: {err_set:#?}");
Err(err_set)
}
}
}
fn debug_result(self, msg: &'static str) -> Self {
match self {
Ok(c) => {
debug!("{msg}: {c:#?}");
Ok(c)
}
Err(err_set) => {
debug!("{msg}: {err_set:#?}");
Err(err_set)
}
}
}
}
#[derive(Debug)]
pub struct Error<W: Warning> {
warning: W,
element: Element,
}
impl<W: Warning> Error<W> {
pub fn warning(&self) -> &W {
&self.warning
}
pub fn into_warning(self) -> W {
self.warning
}
pub fn element(&self) -> &Element {
&self.element
}
pub fn parts(&self) -> (&W, &Element) {
(&self.warning, &self.element)
}
pub fn into_parts(self) -> (W, Element) {
let Self { warning, element } = self;
(warning, element)
}
fn into_other<WA>(self) -> Error<WA>
where
W: Into<WA>,
WA: Warning,
{
let Self { warning, element } = self;
Error {
warning: warning.into(),
element,
}
}
}
impl<W: Warning> std::error::Error for Error<W> {}
impl<W: Warning> fmt::Display for Error<W> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"A warning for element at `{}` was upgraded to an `error`: {}",
self.element.path, self.warning
)
}
}
pub trait WithElement<T, W: Warning> {
fn with_element(self, element: &json::Element<'_>) -> Verdict<T, W>;
}
impl<T, W: Warning> WithElement<T, W> for VerdictDeferred<T, W> {
fn with_element(self, element: &json::Element<'_>) -> Verdict<T, W> {
match self {
Ok(v) => {
let CaveatDeferred { value, warnings } = v;
let SetDeferred(warnings) = warnings;
let warnings = if warnings.is_empty() {
BTreeMap::new()
} else {
let warnings = Group {
element: element.into(),
warnings,
};
BTreeMap::from([(element.id(), warnings)])
};
Ok(Caveat {
value,
warnings: Set(warnings),
})
}
Err(set) => {
let ErrorSetDeferred { error, warnings } = set;
let warnings = Group {
element: element.into(),
warnings,
};
let warnings = BTreeMap::from([(element.id(), warnings)]);
Err(ErrorSet {
error: Box::new(Error {
warning: error,
element: Element::from(element),
}),
warnings,
})
}
}
}
}
#[derive(Debug)]
pub struct Element {
id: json::ElemId,
span: json::parser::Span,
path: Path,
}
impl Element {
pub fn span(&self) -> json::parser::Span {
self.span
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn into_parts(self) -> (json::parser::Span, Path) {
let Self { id: _, span, path } = self;
(span, path)
}
}
impl<'buf> From<&json::Element<'buf>> for Element {
fn from(elem: &json::Element<'buf>) -> Self {
Self {
id: elem.id(),
span: elem.span(),
path: Path(elem.path().to_string()),
}
}
}
pub struct SourceReportIter<'caller, 'buf, W: Warning> {
report_iter: Iter<'caller, W>,
json: &'buf str,
line: u32,
byte: usize,
}
impl<'caller, 'buf, W: Warning> SourceReportIter<'caller, 'buf, W> {
pub fn new(json: &'buf str, report_iter: Iter<'caller, W>) -> Self {
Self {
report_iter,
json,
line: 1,
byte: 0,
}
}
}
impl<'caller, 'buf, W: Warning> Iterator for SourceReportIter<'caller, 'buf, W> {
type Item = ElementReport<'caller, 'buf, W>;
#[expect(
clippy::string_slice,
reason = "The disconnection between the source JSON and the `Element` will be fixed in a future PR"
)]
fn next(&mut self) -> Option<Self::Item> {
let group = self.report_iter.next()?;
let (element, warnings) = group.to_parts();
let lead_in = &self.json[self.byte..element.span.start];
let json::LineCol { line, col } = json::line_col(lead_in);
let json = &self.json[element.span.start..element.span.end];
self.byte = element.span.end;
self.line = self.line.checked_add(line)?;
Some(ElementReport {
element_path: element.path(),
warnings,
json,
location: json::LineCol {
line: self.line,
col: col.saturating_add(1),
},
})
}
}
#[derive(Debug)]
pub struct ElementReport<'caller, 'buf, W: Warning> {
pub element_path: &'caller Path,
pub warnings: Vec<&'caller W>,
pub json: &'buf str,
pub location: json::LineCol,
}
pub struct SetWriter<'caller, W: Warning> {
warnings: &'caller Set<W>,
indent: &'caller str,
}
impl<'caller, W: Warning> SetWriter<'caller, W> {
pub fn new(warnings: &'caller Set<W>) -> Self {
Self {
warnings,
indent: " - ",
}
}
pub fn with_indent(warnings: &'caller Set<W>, indent: &'caller str) -> Self {
Self { warnings, indent }
}
}
impl<W: Warning> fmt::Debug for SetWriter<'_, W> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl<W: Warning> fmt::Display for SetWriter<'_, W> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut iter = self.warnings.iter();
{
let Some((element, warnings)) = iter.next().map(|g| g.to_parts()) else {
return Ok(());
};
writeln!(f, "{}", element.path)?;
for warning in warnings {
write!(f, "{}{}", self.indent, warning)?;
}
}
for (element, warnings) in iter.map(|g| g.to_parts()) {
writeln!(f, "\n{}", element.path)?;
for warning in warnings {
write!(f, "{}{}", self.indent, warning)?;
}
}
Ok(())
}
}
pub trait Warning: Sized + fmt::Debug + fmt::Display + Send + Sync {
fn id(&self) -> Id;
}
#[derive(Debug)]
struct Source<W: Warning> {
#[cfg(test)]
location: &'static std::panic::Location<'static>,
warning: W,
}
impl<W: Warning> Source<W> {
#[track_caller]
fn new(warning: W) -> Self {
#[cfg(test)]
{
Self {
location: std::panic::Location::caller(),
warning,
}
}
#[expect(
clippy::cfg_not_test,
reason = "This is code that is designed for use in tests"
)]
#[cfg(not(test))]
{
Self { warning }
}
}
fn into_warning(self) -> W {
self.warning
}
fn into_other<WA>(self) -> Source<WA>
where
W: Into<WA>,
WA: Warning,
{
#[cfg(test)]
{
let Self {
location: source,
warning,
} = self;
Source {
location: source,
warning: warning.into(),
}
}
#[expect(
clippy::cfg_not_test,
reason = "This is code that is designed for use in tests"
)]
#[cfg(not(test))]
{
let Self { warning } = self;
Source {
warning: warning.into(),
}
}
}
}
impl<W: Warning> Deref for Source<W> {
type Target = W;
fn deref(&self) -> &Self::Target {
&self.warning
}
}
#[derive(Debug)]
pub(crate) struct SetDeferred<W: Warning>(Vec<Source<W>>);
impl<W: Warning> SetDeferred<W> {
pub(crate) fn new() -> Self {
Self(Vec::new())
}
#[track_caller]
pub(crate) fn bail<T>(self, warning: W) -> VerdictDeferred<T, W> {
let Self(warnings) = self;
Err(ErrorSetDeferred {
error: warning,
warnings,
})
}
fn extend<WA>(&mut self, warnings: SetDeferred<WA>)
where
WA: Into<W> + Warning,
{
let SetDeferred(warnings) = warnings;
self.0.extend(warnings.into_iter().map(Source::into_other));
}
}
#[derive(Debug)]
pub struct ErrorSetDeferred<W: Warning> {
error: W,
warnings: Vec<Source<W>>,
}
impl<W: Warning> ErrorSetDeferred<W> {
pub(crate) fn with_warn(warning: W) -> Self {
Self {
warnings: Vec::new(),
error: warning,
}
}
pub(crate) fn into_other<WA>(self) -> ErrorSetDeferred<WA>
where
W: Into<WA>,
WA: Warning,
{
let Self { error, warnings } = self;
let warnings = warnings.into_iter().map(Source::into_other).collect();
ErrorSetDeferred {
error: error.into(),
warnings,
}
}
}
#[derive(Debug)]
pub struct Set<W: Warning>(BTreeMap<json::ElemId, Group<W>>);
impl<W: Warning> Set<W> {
pub(crate) fn new() -> Self {
Self(BTreeMap::new())
}
#[track_caller]
pub(crate) fn insert(&mut self, warning: W, element: &json::Element<'_>) {
self.insert_warning(warning, element.id(), || Element::from(element));
}
#[track_caller]
fn insert_warning<F>(&mut self, warning: W, elem_id: json::ElemId, f: F)
where
F: FnOnce() -> Element,
{
use std::collections::btree_map::Entry;
match self.0.entry(elem_id) {
Entry::Vacant(entry) => {
let element = f();
entry.insert_entry(Group {
element,
warnings: vec![Source::new(warning)],
});
}
Entry::Occupied(mut entry) => {
entry.get_mut().warnings.push(Source::new(warning));
}
}
}
#[track_caller]
pub(crate) fn bail<T>(self, warning: W, element: &json::Element<'_>) -> Verdict<T, W> {
let Self(warnings) = self;
Err(ErrorSet {
error: Box::new(Error {
warning,
element: Element::from(element),
}),
warnings,
})
}
pub(crate) fn into_other<WA>(self) -> Set<WA>
where
W: Into<WA>,
WA: Warning,
{
let Set(warnings) = self;
let warnings = warnings
.into_iter()
.map(|(elem_id, group)| (elem_id, group.into_other()))
.collect();
Set(warnings)
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len_elements(&self) -> usize {
self.0.len()
}
pub fn len_warnings(&self) -> usize {
self.0
.values()
.fold(0, |acc, group| acc.saturating_add(group.warnings.len()))
}
pub fn iter(&self) -> Iter<'_, W> {
Iter {
warnings: self.0.iter(),
}
}
pub fn path_map(&self) -> BTreeMap<&str, Vec<&W>> {
self.0
.values()
.map(|Group { element, warnings }| {
let path = element.path.as_str();
let warnings = warnings.iter().map(|w| &**w).collect();
(path, warnings)
})
.collect()
}
pub fn into_path_map(self) -> BTreeMap<Path, Vec<W>> {
self.0
.into_values()
.map(|Group { element, warnings }| {
let warnings = warnings.into_iter().map(Source::into_warning).collect();
(element.path, warnings)
})
.collect()
}
pub fn path_id_map(&self) -> BTreeMap<&str, Vec<Id>> {
self.0
.values()
.map(|group| {
let warnings = group.warnings.iter().map(|w| w.id()).collect();
(group.element.path.as_str(), warnings)
})
.collect()
}
pub fn path_msg_map(&self) -> BTreeMap<&str, Vec<String>> {
self.0
.values()
.map(|group| {
let warnings = group.warnings.iter().map(|w| w.to_string()).collect();
(group.element.path.as_str(), warnings)
})
.collect()
}
pub(crate) fn deescalate_error(&mut self, err_set: ErrorSet<W>) {
let ErrorSet { error, warnings } = err_set;
let Error { warning, element } = *error;
self.0.extend(warnings);
self.insert_warning(warning, element.id, || element);
}
fn extend<WA>(&mut self, warnings: Set<WA>)
where
WA: Into<W> + Warning,
{
use std::collections::btree_map::Entry;
let Set(warnings) = warnings;
let warnings = warnings
.into_iter()
.map(|(elem_id, group)| (elem_id, group.into_other()));
for (elem_id, group) in warnings {
match self.0.entry(elem_id) {
Entry::Vacant(entry) => {
entry.insert_entry(group);
}
Entry::Occupied(mut entry) => {
let Group {
element: _,
warnings,
} = group;
entry.get_mut().warnings.extend(warnings);
}
}
}
}
}
#[derive(Debug)]
pub struct ErrorSet<W: Warning> {
error: Box<Error<W>>,
warnings: BTreeMap<json::ElemId, Group<W>>,
}
impl<W> ErrorSet<W>
where
W: Warning,
{
pub(crate) fn with_warn(warning: W, element: &json::Element<'_>) -> Self {
Self {
warnings: BTreeMap::new(),
error: Box::new(Error {
warning,
element: Element::from(element),
}),
}
}
pub fn into_parts(self) -> (Error<W>, Set<W>) {
let Self { error, warnings } = self;
(*error, Set(warnings))
}
pub(crate) fn into_other<WA>(self) -> ErrorSet<WA>
where
W: Into<WA>,
WA: Warning,
{
let Self { error, warnings } = self;
let warnings = warnings
.into_iter()
.map(|(elem_id, group)| (elem_id, group.into_other()))
.collect();
ErrorSet {
error: Box::new(Error::into_other(*error)),
warnings,
}
}
}
#[derive(Debug)]
pub struct Group<W: Warning> {
element: Element,
warnings: Vec<Source<W>>,
}
impl<W> Group<W>
where
W: Warning,
{
pub fn into_parts(self) -> (Element, Vec<W>) {
let Self { element, warnings } = self;
let warnings = warnings.into_iter().map(Source::into_warning).collect();
(element, warnings)
}
pub fn to_parts(&self) -> (&Element, Vec<&W>) {
let Self { element, warnings } = self;
let warnings = warnings.iter().map(|w| &**w).collect();
(element, warnings)
}
pub fn warnings(&self) -> Vec<&W> {
self.warnings.iter().map(|w| &**w).collect()
}
pub fn into_warnings(self) -> Vec<W> {
let Self {
element: _,
warnings,
} = self;
warnings.into_iter().map(Source::into_warning).collect()
}
fn into_other<WA>(self) -> Group<WA>
where
W: Into<WA>,
WA: Warning,
{
let Self { element, warnings } = self;
let warnings = warnings.into_iter().map(Source::into_other).collect();
Group { element, warnings }
}
}
pub struct Iter<'caller, W>
where
W: Warning,
{
warnings: std::collections::btree_map::Iter<'caller, json::ElemId, Group<W>>,
}
impl<W> Iter<'_, W> where W: Warning {}
impl<'caller, W: Warning> Iterator for Iter<'caller, W> {
type Item = &'caller Group<W>;
fn next(&mut self) -> Option<Self::Item> {
let (_elem_id, group) = self.warnings.next()?;
Some(group)
}
}
pub struct IntoIter<W>
where
W: Warning,
{
warnings: std::collections::btree_map::IntoIter<json::ElemId, Group<W>>,
}
impl<W: Warning> Iterator for IntoIter<W> {
type Item = Group<W>;
fn next(&mut self) -> Option<Self::Item> {
let (_elem_id, group) = self.warnings.next()?;
Some(group)
}
}
impl<W: Warning> IntoIterator for Set<W> {
type Item = Group<W>;
type IntoIter = IntoIter<W>;
fn into_iter(self) -> Self::IntoIter {
let Set(warnings) = self;
IntoIter {
warnings: warnings.into_iter(),
}
}
}
impl<'a, W: Warning> IntoIterator for &'a Set<W> {
type Item = &'a Group<W>;
type IntoIter = Iter<'a, W>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}