val 0.4.2

An arbitrary precision calculator language
Documentation
use super::*;

#[derive(Debug, PartialEq, thiserror::Error)]
pub enum Error {
  #[error("division by zero")]
  DivisionByZero,
  #[error("exit requested with code {code}")]
  Exit { code: i32, span: Span },
  #[error("invalid decimal")]
  InvalidDecimal,
  #[error("{0}")]
  Message(String),
  #[error("modulo by zero")]
  ModuloByZero,
  #[error("{error}")]
  Spanned { error: Box<Self>, span: Span },
  #[error("zero cannot be raised to a negative power")]
  ZeroToNegativePower,
}

impl Error {
  pub fn new(span: Span, message: impl Into<String>) -> Self {
    Self::Message(message.into()).with_span(span)
  }

  #[must_use]
  pub fn report<'a>(&self, id: &'a str) -> Report<'a, (&'a str, Range<usize>)> {
    let span_range = self.span().into_range();

    let mut report = Report::build(
      ReportKind::Custom("error", Color::Red),
      (id, span_range.clone()),
    )
    .with_config(ariadne::Config::new().with_index_type(IndexType::Byte))
    .with_message(self.to_string());

    report = report.with_label(
      Label::new((id, span_range))
        .with_message(self.to_string())
        .with_color(Color::Red),
    );

    report.finish()
  }

  #[must_use]
  pub fn span(&self) -> Span {
    match self {
      Self::Exit { span, .. } | Self::Spanned { span, .. } => *span,
      _ => Span::from(0..0),
    }
  }

  #[must_use]
  pub fn with_span(self, span: Span) -> Self {
    match self {
      Self::Exit { code, .. } => Self::Exit { code, span },
      Self::Spanned { error, .. } => Self::Spanned { error, span },
      error => Self::Spanned {
        error: Box::new(error),
        span,
      },
    }
  }
}