use std::{
iter::FromIterator,
ops::{ControlFlow, FromResidual, Try},
panic::Location,
result::Result::{self as SResult, Err as SErr, Ok as SOk},
};
use crate::{ast, format_error, source_code};
type LocationStatic = &'static Location<'static>;
pub enum Loc {
Location(LocationStatic),
Parent {
location: LocationStatic,
genre: ast::Genre,
},
}
impl Loc {
pub fn location(&self) -> LocationStatic {
match self {
Loc::Location(location) => location,
Loc::Parent { location, .. } => location,
}
}
pub fn related_code(&self) -> source_code::LineSpan {
match self {
Loc::Location(location) => source_code::code_from_location(location, None),
Loc::Parent { location, genre } => source_code::code_from_location(location, Some(genre)),
}
}
}
impl std::fmt::Debug for Loc {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let location = match self {
Self::Location(location) => location,
Self::Parent { location, .. } => location,
};
writeln!(f, "{}", location)
}
}
#[derive(Default, PartialEq, Eq)]
pub enum Severity {
Trace,
Debug,
Info,
Warn,
#[default]
Error,
Off,
}
impl std::fmt::Display for Severity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Severity::Trace => write!(f, "trace"),
Severity::Debug => write!(f, "debug"),
Severity::Info => write!(f, "info"),
Severity::Warn => write!(f, "warning"),
Severity::Error => write!(f, "error"),
Severity::Off => write!(f, "off"),
}
}
}
pub struct Label {
pub loc: Loc,
pub label: String,
}
pub struct Group {
pub loc: Loc,
pub errors: Errors,
}
pub struct Error {
pub code: &'static str,
pub url: Option<String>,
pub severity: Severity,
pub display: String,
pub loc: Loc,
pub groupe: Option<Group>,
pub labels: Vec<Label>,
pub source: Option<Box<Error>>,
}
impl std::fmt::Debug for Error {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let stderr = &mut std::io::stderr();
format_error::SimpleReportHandler::new()
.render_error(stderr, self)
.unwrap();
std::fmt::Result::Ok(())
}
}
#[derive(Default)]
pub struct Errors(pub Vec<Error>);
impl std::fmt::Debug for Errors {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for error in &self.0 {
write!(f, "{error:?}")?;
}
SOk(())
}
}
pub enum Result<T> {
Ok(T),
Err(Errors),
}
use Result::{Err, Ok};
impl<T> Result<T> {
#[allow(dead_code)]
fn into_result(self) -> SResult<T, Errors> {
match self {
Ok(value) => SOk(value),
Err(errors) => SErr(errors),
}
}
}
impl<E: ToString + 'static> From<E> for Errors {
#[track_caller]
fn from(value: E) -> Self {
Self(vec![Error {
code: std::any::type_name::<E>(),
display: value.to_string(),
loc: Loc::Location(Location::caller()),
url: None,
severity: Severity::default(),
labels: Default::default(),
groupe: None,
source: None,
}])
}
}
impl<T> Try for Result<T> {
type Output = T;
type Residual = Result<T>;
fn from_output(output: Self::Output) -> Self {
Ok(output)
}
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
match self {
Ok(value) => ControlFlow::Continue(value),
Err(errors) => ControlFlow::Break(Err(errors)),
}
}
}
impl<T, E: Into<Errors>, R> FromResidual<SResult<T, E>> for Result<R> {
#[track_caller]
fn from_residual(residual: SResult<T, E>) -> Self {
match residual {
SErr(errors) => Err(errors.into()),
_ => unreachable!("FromResidual got Ok.."),
}
}
}
impl<T, R> FromResidual<Result<T>> for Result<R> {
fn from_residual(residual: Result<T>) -> Self {
match residual {
Err(errors) => Err(errors),
_ => unreachable!("FromResidual got Ok.."),
}
}
}
impl<T> FromIterator<Result<T>> for Result<Vec<T>> {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = Result<T>>,
{
let mut errors = Errors::default();
let mut results = Vec::new();
for item in iter {
match item {
Ok(value) => results.push(value),
Err(e) => errors.0.extend(e.0),
}
}
if errors.0.is_empty() { Ok(results) } else { Err(errors) }
}
}