use crate::ast;
use crate::ast::{Pretty, Range};
use crate::compile::schema::MType;
use crate::error::MultiError;
pub use crate::parser::error::ErrorLocation;
use crate::parser::error::{ParserError, PrettyError};
use crate::runtime::error::RuntimeError;
use crate::types::error::TypesystemError;
use colored::*;
use snafu::{Backtrace, Snafu};
use std::io;
pub type Result<T, E = CompileError> = std::result::Result<T, E>;
#[derive(Debug, Snafu)]
#[snafu(visibility(pub))]
pub enum CompileError {
#[snafu(display("Syntax error: {}", source), context(false))]
SyntaxError {
#[snafu(backtrace)]
source: ParserError,
},
#[snafu(display("Internal error: {}", what))]
InternalError {
what: String,
backtrace: Option<Backtrace>,
loc: ErrorLocation,
},
#[snafu(display("Internal error: {}", what))]
ExternalError {
what: String,
backtrace: Option<Backtrace>,
},
#[snafu(display("Typesystem error: {}", source))]
TypesystemError {
#[snafu(backtrace)]
source: TypesystemError,
loc: ErrorLocation,
},
#[snafu(display("Runtime error: {}", source))]
RuntimeError {
#[snafu(backtrace)]
source: RuntimeError,
loc: ErrorLocation,
},
#[snafu()]
FsError {
source: io::Error,
backtrace: Option<Backtrace>,
loc: ErrorLocation,
},
#[snafu(display("Unimplemented: {}", what))]
Unimplemented {
what: String,
backtrace: Option<Backtrace>,
loc: ErrorLocation,
},
#[snafu(display("Missing argument: {}", path.pretty()))]
MissingArg {
path: ast::Path,
backtrace: Option<Backtrace>,
},
#[snafu(display("Duplicate entry: {}", path.pretty()))]
DuplicateEntry {
path: ast::Path,
backtrace: Option<Backtrace>,
},
#[snafu(display("No such entry: {}", path.pretty()))]
NoSuchEntry {
path: ast::Path,
backtrace: Option<Backtrace>,
},
#[snafu(display(
"Wrong kind: expected {} declaration at {}, found {}",
expected.white().bold(),
path.pretty(),
kind.white().bold(),
))]
WrongKind {
path: ast::Path,
expected: String,
kind: String,
backtrace: Option<Backtrace>,
},
#[snafu(display("Type mismatch: found {} not {}", rhs.pretty(), lhs.pretty()))]
WrongType {
lhs: MType,
rhs: MType,
backtrace: Option<Backtrace>,
},
#[snafu(display("Types cannot be coerced: {}", types.iter().map(Pretty::pretty).collect::<Vec<_>>().join(", ")))]
CoercionError {
types: Vec<MType>,
backtrace: Option<Backtrace>,
loc: ErrorLocation,
},
#[snafu(display("Error importing {}: {}", path.pretty(), what))]
ImportError {
path: ast::Path,
what: String,
backtrace: Option<Backtrace>,
},
#[snafu(display("Invalid scalar subselect: {}", what))]
ScalarSubselectError {
what: String,
backtrace: Option<Backtrace>,
loc: ErrorLocation,
},
#[snafu(display("Invalid connection: {}", what))]
InvalidConnectionError {
what: String,
backtrace: Option<Backtrace>,
loc: ErrorLocation,
},
#[snafu(display("Invalid foreach: {}", what))]
InvalidForEachError {
what: String,
backtrace: Option<Backtrace>,
loc: ErrorLocation,
},
#[snafu(display("{}", sources.first().unwrap()))]
Multiple {
sources: Vec<CompileError>,
},
}
impl CompileError {
pub fn unimplemented(loc: ErrorLocation, what: &str) -> CompileError {
return UnimplementedSnafu { loc, what }.build();
}
pub fn missing_arg(path: ast::Path) -> CompileError {
return MissingArgSnafu { path }.build();
}
pub fn no_such_entry(path: ast::Path) -> CompileError {
return NoSuchEntrySnafu { path }.build();
}
pub fn duplicate_entry(path: ast::Path) -> CompileError {
return DuplicateEntrySnafu { path }.build();
}
pub fn wrong_kind(path: ast::Path, expected: &str, kind: &str) -> CompileError {
return WrongKindSnafu {
path,
expected,
kind,
}
.build();
}
pub fn wrong_type(lhs: &MType, rhs: &MType) -> CompileError {
return WrongTypeSnafu {
lhs: lhs.clone(),
rhs: rhs.clone(),
}
.build();
}
pub fn coercion(loc: ErrorLocation, types: &[MType]) -> CompileError {
return CoercionSnafu {
loc,
types: types.to_vec(),
}
.build();
}
pub fn import_error(path: ast::Path, what: &str) -> CompileError {
return ImportSnafu {
path,
what: what.to_string(),
}
.build();
}
pub fn scalar_subselect(loc: ErrorLocation, what: &str) -> CompileError {
return ScalarSubselectSnafu {
loc,
what: what.to_string(),
}
.build();
}
pub fn invalid_conn(loc: ErrorLocation, what: &str) -> CompileError {
return InvalidConnectionSnafu {
loc,
what: what.to_string(),
}
.build();
}
pub fn invalid_foreach(loc: ErrorLocation, what: &str) -> CompileError {
return InvalidForEachSnafu {
loc,
what: what.to_string(),
}
.build();
}
pub fn internal(loc: ErrorLocation, what: &str) -> CompileError {
return InternalSnafu {
loc,
what: what.to_string(),
}
.build();
}
pub fn external(what: &str) -> CompileError {
return ExternalSnafu {
what: what.to_string(),
}
.build();
}
}
pub fn path_location(path: &Vec<ast::Located<ast::Ident>>) -> ErrorLocation {
if path.len() == 0 {
return ErrorLocation::Unknown;
}
let (start_file, start) = match path[0].location() {
ErrorLocation::Range(file, range) => (file, Some(&range.start)),
ErrorLocation::Single(file, _) => (file, None),
ErrorLocation::File(file) => (file, None),
ErrorLocation::Unknown => return ErrorLocation::Unknown,
};
let (end_file, end) = match &path[path.len() - 1].location() {
ErrorLocation::Range(file, range) => (file, Some(&range.end)),
ErrorLocation::Single(file, _) => (file, None),
ErrorLocation::File(file) => (file, None),
ErrorLocation::Unknown => return ErrorLocation::Unknown,
};
if start_file != end_file {
return ErrorLocation::Unknown;
}
if start.is_none() || end.is_none() {
return ErrorLocation::File(start_file.clone());
}
ErrorLocation::Range(
start_file.clone(),
Range {
start: start.unwrap().clone(),
end: end.unwrap().clone(),
},
)
}
impl PrettyError for CompileError {
fn location(&self) -> ErrorLocation {
match self {
CompileError::SyntaxError { source } => source.location(),
CompileError::InternalError { loc, .. } => loc.clone(),
CompileError::ExternalError { .. } => ErrorLocation::File("<qs>".to_string()),
CompileError::TypesystemError { loc, .. } => loc.clone(),
CompileError::RuntimeError { loc, .. } => loc.clone(),
CompileError::FsError { loc, .. } => loc.clone(),
CompileError::Unimplemented { loc, .. } => loc.clone(),
CompileError::MissingArg { path, .. } => path_location(path),
CompileError::DuplicateEntry { path, .. } => path_location(path),
CompileError::NoSuchEntry { path, .. } => path_location(path),
CompileError::WrongKind { path, .. } => path_location(path),
CompileError::WrongType { lhs, .. } => lhs.location(),
CompileError::CoercionError { loc, .. } => loc.clone(),
CompileError::ImportError { path, .. } => path_location(path),
CompileError::ScalarSubselectError { loc, .. } => loc.clone(),
CompileError::InvalidConnectionError { loc, .. } => loc.clone(),
CompileError::InvalidForEachError { loc, .. } => loc.clone(),
CompileError::Multiple { sources } => sources.first().unwrap().location(),
}
}
}
impl<Guard> From<std::sync::PoisonError<Guard>> for CompileError {
fn from(e: std::sync::PoisonError<Guard>) -> CompileError {
CompileError::external(format!("{}", e).as_str())
}
}
impl From<tokio::task::JoinError> for CompileError {
fn from(e: tokio::task::JoinError) -> CompileError {
CompileError::external(format!("{}", e).as_str())
}
}
impl From<tokio::sync::oneshot::error::RecvError> for CompileError {
fn from(e: tokio::sync::oneshot::error::RecvError) -> CompileError {
CompileError::external(format!("{}", e).as_str())
}
}
impl
From<std::sync::TryLockError<std::sync::RwLockWriteGuard<'_, tokio::sync::watch::Receiver<()>>>>
for CompileError
{
fn from(
e: std::sync::TryLockError<
std::sync::RwLockWriteGuard<'_, tokio::sync::watch::Receiver<()>>,
>,
) -> CompileError {
CompileError::external(format!("{}", e).as_str())
}
}
impl From<tokio::sync::watch::error::RecvError> for CompileError {
fn from(e: tokio::sync::watch::error::RecvError) -> CompileError {
CompileError::external(format!("{}", e).as_str())
}
}
impl From<std::io::Error> for CompileError {
fn from(e: std::io::Error) -> CompileError {
CompileError::external(format!("{}", e).as_str())
}
}
impl MultiError for CompileError {
fn new_multi_error(errs: Vec<Self>) -> Self {
CompileError::Multiple { sources: errs }
}
fn into_errors(self) -> Vec<Self> {
match self {
CompileError::Multiple { sources } => {
sources.into_iter().flat_map(|e| e.into_errors()).collect()
}
CompileError::SyntaxError { source } => {
source.into_errors().into_iter().map(Into::into).collect()
}
_ => vec![self],
}
}
}