use alloc::borrow::{Cow, ToOwned};
use alloc::string::String;
use alloc::{vec, vec::Vec};
use core::ops::Range;
use crate::Level;
use crate::renderer::source_map::{TrimmedPatch, as_substr};
pub(crate) const ERROR_TXT: &str = "error";
pub(crate) const HELP_TXT: &str = "help";
pub(crate) const INFO_TXT: &str = "info";
pub(crate) const NOTE_TXT: &str = "note";
pub(crate) const WARNING_TXT: &str = "warning";
pub type Report<'a> = &'a [Group<'a>];
#[derive(Clone, Debug, Default)]
pub(crate) struct Id<'a> {
pub(crate) id: Option<Cow<'a, str>>,
pub(crate) url: Option<Cow<'a, str>>,
}
#[doc = include_str!("../examples/highlight_message.rs")]
#[doc = include_str!("../examples/highlight_message.svg")]
#[derive(Clone, Debug)]
pub struct Group<'a> {
pub(crate) primary_level: Level<'a>,
pub(crate) title: Option<Title<'a>>,
pub(crate) elements: Vec<Element<'a>>,
}
impl<'a> Group<'a> {
pub fn with_title(title: Title<'a>) -> Self {
let level = title.level.clone();
let mut x = Self::with_level(level);
x.title = Some(title);
x
}
#[doc = include_str!("../examples/elide_header.rs")]
#[doc = include_str!("../examples/elide_header.svg")]
pub fn with_level(level: Level<'a>) -> Self {
Self {
primary_level: level,
title: None,
elements: vec![],
}
}
pub fn element(mut self, section: impl Into<Element<'a>>) -> Self {
self.elements.push(section.into());
self
}
pub fn elements(mut self, sections: impl IntoIterator<Item = impl Into<Element<'a>>>) -> Self {
self.elements.extend(sections.into_iter().map(Into::into));
self
}
pub fn is_empty(&self) -> bool {
self.elements.is_empty() && self.title.is_none()
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum Element<'a> {
Message(Message<'a>),
Cause(Snippet<'a, Annotation<'a>>),
Suggestion(Snippet<'a, Patch<'a>>),
Origin(Origin<'a>),
Padding(Padding),
}
impl<'a> From<Message<'a>> for Element<'a> {
fn from(value: Message<'a>) -> Self {
Element::Message(value)
}
}
impl<'a> From<Snippet<'a, Annotation<'a>>> for Element<'a> {
fn from(value: Snippet<'a, Annotation<'a>>) -> Self {
Element::Cause(value)
}
}
impl<'a> From<Snippet<'a, Patch<'a>>> for Element<'a> {
fn from(value: Snippet<'a, Patch<'a>>) -> Self {
Element::Suggestion(value)
}
}
impl<'a> From<Origin<'a>> for Element<'a> {
fn from(value: Origin<'a>) -> Self {
Element::Origin(value)
}
}
impl From<Padding> for Element<'_> {
fn from(value: Padding) -> Self {
Self::Padding(value)
}
}
#[derive(Clone, Debug)]
pub struct Padding;
#[derive(Clone, Debug)]
pub struct Title<'a> {
pub(crate) level: Level<'a>,
pub(crate) id: Option<Id<'a>>,
pub(crate) text: Cow<'a, str>,
pub(crate) allows_styling: bool,
}
impl<'a> Title<'a> {
pub fn id(mut self, id: impl Into<Cow<'a, str>>) -> Self {
self.id.get_or_insert(Id::default()).id = Some(id.into());
self
}
pub fn id_url(mut self, url: impl Into<Cow<'a, str>>) -> Self {
self.id.get_or_insert(Id::default()).url = Some(url.into());
self
}
pub fn element(self, section: impl Into<Element<'a>>) -> Group<'a> {
Group::with_title(self).element(section)
}
pub fn elements(self, sections: impl IntoIterator<Item = impl Into<Element<'a>>>) -> Group<'a> {
Group::with_title(self).elements(sections)
}
}
#[derive(Clone, Debug)]
pub struct Message<'a> {
pub(crate) level: Level<'a>,
pub(crate) text: Cow<'a, str>,
}
#[derive(Clone, Debug)]
pub struct Snippet<'a, T> {
pub(crate) path: Option<Cow<'a, str>>,
pub(crate) line_start: usize,
pub(crate) source: Cow<'a, str>,
pub(crate) markers: Vec<T>,
pub(crate) fold: bool,
}
impl<'a, T: Clone> Snippet<'a, T> {
pub fn source(source: impl Into<Cow<'a, str>>) -> Self {
Self {
path: None,
line_start: 1,
source: source.into(),
markers: vec![],
fold: true,
}
}
pub fn line_start(mut self, line_start: usize) -> Self {
self.line_start = line_start;
self
}
pub fn path(mut self, path: impl Into<OptionCow<'a>>) -> Self {
self.path = path.into().0;
self
}
pub fn fold(mut self, fold: bool) -> Self {
self.fold = fold;
self
}
}
impl<'a> Snippet<'a, Annotation<'a>> {
pub fn annotation(mut self, annotation: Annotation<'a>) -> Snippet<'a, Annotation<'a>> {
self.markers.push(annotation);
self
}
pub fn annotations(mut self, annotation: impl IntoIterator<Item = Annotation<'a>>) -> Self {
self.markers.extend(annotation);
self
}
}
impl<'a> Snippet<'a, Patch<'a>> {
pub fn patch(mut self, patch: Patch<'a>) -> Snippet<'a, Patch<'a>> {
self.markers.push(patch);
self
}
pub fn patches(mut self, patches: impl IntoIterator<Item = Patch<'a>>) -> Self {
self.markers.extend(patches);
self
}
}
#[doc = include_str!("../examples/expected_type.rs")]
#[doc = include_str!("../examples/expected_type.svg")]
#[derive(Clone, Debug)]
pub struct Annotation<'a> {
pub(crate) span: Range<usize>,
pub(crate) label: Option<Cow<'a, str>>,
pub(crate) kind: AnnotationKind,
pub(crate) highlight_source: bool,
}
impl<'a> Annotation<'a> {
pub fn label(mut self, label: impl Into<OptionCow<'a>>) -> Self {
self.label = label.into().0;
self
}
pub fn highlight_source(mut self, highlight_source: bool) -> Self {
self.highlight_source = highlight_source;
self
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum AnnotationKind {
Primary,
Context,
#[doc = include_str!("../examples/struct_name_as_context.rs")]
#[doc = include_str!("../examples/struct_name_as_context.svg")]
Visible,
}
impl AnnotationKind {
pub fn span<'a>(self, span: Range<usize>) -> Annotation<'a> {
Annotation {
span,
label: None,
kind: self,
highlight_source: false,
}
}
pub(crate) fn is_primary(&self) -> bool {
matches!(self, AnnotationKind::Primary)
}
}
#[doc = include_str!("../examples/multi_suggestion.rs")]
#[doc = include_str!("../examples/multi_suggestion.svg")]
#[derive(Clone, Debug)]
pub struct Patch<'a> {
pub(crate) span: Range<usize>,
pub(crate) replacement: Cow<'a, str>,
}
impl<'a> Patch<'a> {
pub fn new(span: Range<usize>, replacement: impl Into<Cow<'a, str>>) -> Self {
Self {
span,
replacement: replacement.into(),
}
}
pub(crate) fn trim_trivial_replacements(self, source: &str) -> TrimmedPatch<'a> {
let mut trimmed = TrimmedPatch {
original_span: self.span.clone(),
span: self.span,
replacement: self.replacement,
};
if trimmed.replacement.is_empty() {
return trimmed;
}
let Some(snippet) = source.get(trimmed.original_span.clone()) else {
return trimmed;
};
if let Some((prefix, substr, suffix)) = as_substr(snippet, &trimmed.replacement) {
trimmed.span = trimmed.original_span.start + prefix
..trimmed.original_span.end.saturating_sub(suffix);
trimmed.replacement = Cow::Owned(substr.to_owned());
}
trimmed
}
}
#[derive(Clone, Debug)]
pub struct Origin<'a> {
pub(crate) path: Cow<'a, str>,
pub(crate) line: Option<usize>,
pub(crate) char_column: Option<usize>,
}
impl<'a> Origin<'a> {
pub fn path(path: impl Into<Cow<'a, str>>) -> Self {
Self {
path: path.into(),
line: None,
char_column: None,
}
}
pub fn line(mut self, line: usize) -> Self {
self.line = Some(line);
self
}
pub fn char_column(mut self, char_column: usize) -> Self {
self.char_column = Some(char_column);
self
}
}
impl<'a> From<Cow<'a, str>> for Origin<'a> {
fn from(origin: Cow<'a, str>) -> Self {
Self::path(origin)
}
}
#[derive(Debug)]
pub struct OptionCow<'a>(pub(crate) Option<Cow<'a, str>>);
impl<'a, T: Into<Cow<'a, str>>> From<Option<T>> for OptionCow<'a> {
fn from(value: Option<T>) -> Self {
Self(value.map(Into::into))
}
}
impl<'a> From<&'a Cow<'a, str>> for OptionCow<'a> {
fn from(value: &'a Cow<'a, str>) -> Self {
Self(Some(Cow::Borrowed(value)))
}
}
impl<'a> From<Cow<'a, str>> for OptionCow<'a> {
fn from(value: Cow<'a, str>) -> Self {
Self(Some(value))
}
}
impl<'a> From<&'a str> for OptionCow<'a> {
fn from(value: &'a str) -> Self {
Self(Some(Cow::Borrowed(value)))
}
}
impl<'a> From<String> for OptionCow<'a> {
fn from(value: String) -> Self {
Self(Some(Cow::Owned(value)))
}
}
impl<'a> From<&'a String> for OptionCow<'a> {
fn from(value: &'a String) -> Self {
Self(Some(Cow::Borrowed(value.as_str())))
}
}