use std::fmt;
use crate::parser::capture::MatchResult;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AnnotationKind {
Primary,
Secondary,
Context,
}
#[derive(Clone, Debug)]
pub struct DiagnosticAnnotation {
pub span: (usize, usize),
pub message: String,
pub kind: AnnotationKind,
}
#[derive(Clone, Debug)]
pub struct InlineError {
pub message: String,
pub span: Option<(usize, usize)>,
pub annotations: Vec<DiagnosticAnnotation>,
pub notes: Vec<String>,
pub helps: Vec<String>,
}
impl InlineError {
#[inline]
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
span: None,
annotations: Vec::new(),
notes: Vec::new(),
helps: Vec::new(),
}
}
#[inline]
pub fn at(span: (usize, usize), message: impl Into<String>) -> Self {
Self::new(message).with_span(Some(span))
}
#[inline]
pub fn reporting_span(&self) -> (usize, usize) {
self.span.unwrap_or((0, 0))
}
pub fn with_span(mut self, span: Option<(usize, usize)>) -> Self {
self.span = span;
self
}
pub fn set_span(&mut self, span: Option<(usize, usize)>) -> &mut Self {
self.span = span;
self
}
pub fn with_note(mut self, note: impl Into<String>) -> Self {
self.notes.push(note.into());
self
}
pub fn add_note(&mut self, note: impl Into<String>) -> &mut Self {
self.notes.push(note.into());
self
}
pub fn with_help(mut self, help: impl Into<String>) -> Self {
self.helps.push(help.into());
self
}
pub fn add_help(&mut self, help: impl Into<String>) -> &mut Self {
self.helps.push(help.into());
self
}
pub fn with_annotation(
mut self,
span: (usize, usize),
message: impl Into<String>,
kind: AnnotationKind,
) -> Self {
self.annotations.push(DiagnosticAnnotation {
span,
message: message.into(),
kind,
});
self
}
pub fn add_annotation(
&mut self,
span: (usize, usize),
message: impl Into<String>,
kind: AnnotationKind,
) -> &mut Self {
self.annotations.push(DiagnosticAnnotation {
span,
message: message.into(),
kind,
});
self
}
#[cfg(feature = "annotate-snippets")]
pub fn eprint(&self, source_id: &str, source_text: &str) {
crate::error::ParserError::Inline(self.clone()).eprint(source_id, source_text);
}
#[cfg(feature = "annotate-snippets")]
pub fn write(&self, source_id: &str, source_text: &str, sink: impl std::io::Write) {
crate::error::ParserError::Inline(self.clone()).write(source_id, source_text, sink);
}
}
impl fmt::Display for InlineError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)?;
if let Some((a, b)) = self.span {
write!(f, " at {a}..{b}")?;
}
for ann in &self.annotations {
write!(f, "\n {}..{}: {}", ann.span.0, ann.span.1, ann.message)?;
}
for note in &self.notes {
write!(f, "\nnote: {note}")?;
}
for help in &self.helps {
write!(f, "\nhelp: {help}")?;
}
Ok(())
}
}
#[derive(Clone, Copy, Debug)]
pub struct MatchDiagCtx {
pub start: usize,
pub end: usize,
}
impl MatchDiagCtx {
#[inline]
pub fn span(&self) -> (usize, usize) {
(self.start, self.end)
}
#[inline]
pub fn insertion_point(pos: usize) -> Self {
Self {
start: pos,
end: pos,
}
}
}
pub trait BuildInlineError<MRes: MatchResult>: Clone {
fn build_inline_error<'snap>(
&self,
ctx: MatchDiagCtx,
snapshot: MRes::Snapshot<'snap>,
) -> InlineError
where
MRes: 'snap;
}
#[inline]
pub fn ctx_factory<F>(f: F) -> F
where
F: Fn(MatchDiagCtx) -> InlineError + Clone,
{
f
}
#[derive(Clone, Debug)]
pub struct MissingSyntax(pub String);
impl<MRes: MatchResult> BuildInlineError<MRes> for MissingSyntax {
#[inline]
fn build_inline_error<'snap>(
&self,
ctx: MatchDiagCtx,
_snapshot: MRes::Snapshot<'snap>,
) -> InlineError
where
MRes: 'snap,
{
InlineError::new(self.0.clone()).with_span(Some((ctx.start, ctx.start)))
}
}
#[derive(Clone, Debug)]
pub struct UnwantedSyntax(pub String);
impl<MRes: MatchResult> BuildInlineError<MRes> for UnwantedSyntax {
#[inline]
fn build_inline_error<'snap>(
&self,
ctx: MatchDiagCtx,
_snapshot: MRes::Snapshot<'snap>,
) -> InlineError
where
MRes: 'snap,
{
InlineError::new(self.0.clone()).with_span(Some((ctx.start, ctx.end)))
}
}
#[derive(Clone)]
pub struct ClosureBuild<F>(pub F);
impl<F> fmt::Debug for ClosureBuild<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ClosureBuild").finish_non_exhaustive()
}
}
impl<MRes: MatchResult, F> BuildInlineError<MRes> for ClosureBuild<F>
where
F: Fn(MatchDiagCtx) -> InlineError + Clone,
{
#[inline]
fn build_inline_error<'snap>(
&self,
ctx: MatchDiagCtx,
_snapshot: MRes::Snapshot<'snap>,
) -> InlineError
where
MRes: 'snap,
{
(self.0)(ctx)
}
}
#[derive(Clone)]
pub struct SnapshotFactory<F>(pub F);
impl<F> fmt::Debug for SnapshotFactory<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SnapshotFactory").finish_non_exhaustive()
}
}
pub trait SnapCallable<'a, MRes>
where
MRes: MatchResult + 'a,
{
fn call(&self, snap: &MRes::Snapshot<'a>, ctx: MatchDiagCtx) -> InlineError;
}
impl<'a, MRes, F> SnapCallable<'a, MRes> for F
where
MRes: MatchResult + 'a,
F: Fn(&MRes::Snapshot<'a>, MatchDiagCtx) -> InlineError,
{
#[inline]
fn call(&self, snap: &MRes::Snapshot<'a>, ctx: MatchDiagCtx) -> InlineError {
self(snap, ctx)
}
}
impl<MRes, F> BuildInlineError<MRes> for SnapshotFactory<F>
where
MRes: MatchResult,
F: Clone + for<'a> SnapCallable<'a, MRes>,
{
#[inline]
fn build_inline_error<'snap>(
&self,
ctx: MatchDiagCtx,
snapshot: MRes::Snapshot<'snap>,
) -> InlineError
where
MRes: 'snap,
{
SnapCallable::<'snap, MRes>::call(&self.0, &snapshot, ctx)
}
}