use std::{
convert::Infallible,
ffi::{NulError, c_void},
fmt::{self, Display, Formatter},
str::Utf8Error,
string::FromUtf8Error,
};
use crate::{
SourceInfo, TableGenParser,
raw::{
TableGenDiagKind::TABLEGEN_DK_ERROR, TableGenSourceLocationRef, tableGenPrintError,
tableGenSourceLocationClone, tableGenSourceLocationFree, tableGenSourceLocationNull,
},
string_ref::StringRef,
util::print_string_callback,
};
#[non_exhaustive]
#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
pub enum TableGenError {
#[error("invalid TableGen source")]
InvalidSource,
#[error("invalid TableGen source")]
InvalidSourceString(#[from] NulError),
#[error("invalid UTF-8 string")]
InvalidUtf8Str(#[from] Utf8Error),
#[error("invalid UTF-8 string")]
InvalidUtf8String(#[from] FromUtf8Error),
#[error("failed to parse TableGen source")]
Parse,
#[error("expected field {0} in record")]
MissingValue(String),
#[error("expected def {0}")]
MissingDef(String),
#[error("expected class {0}")]
MissingClass(String),
#[error("invalid conversion from {from} to {to}")]
InitConversion {
from: &'static str,
to: &'static str,
},
#[error("invalid source location")]
InvalidSourceLocation,
#[error("infallible")]
Infallible(#[from] Infallible),
}
#[derive(Debug, PartialEq, Eq)]
pub struct SourceLocation {
raw: TableGenSourceLocationRef,
}
unsafe impl Sync for SourceLocation {}
unsafe impl Send for SourceLocation {}
impl SourceLocation {
pub unsafe fn from_raw(raw: TableGenSourceLocationRef) -> Self {
Self { raw }
}
pub fn none() -> Self {
unsafe {
Self {
raw: tableGenSourceLocationNull(),
}
}
}
}
impl Clone for SourceLocation {
fn clone(&self) -> Self {
unsafe { Self::from_raw(tableGenSourceLocationClone(self.raw)) }
}
}
impl Drop for SourceLocation {
fn drop(&mut self) {
unsafe { tableGenSourceLocationFree(self.raw) }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SourceError<E> {
location: SourceLocation,
message: Option<String>,
error: E,
}
impl<E: std::error::Error> SourceError<E> {
pub fn new(location: SourceLocation, error: E) -> Self {
Self {
location,
error,
message: None,
}
}
pub fn location(&self) -> &SourceLocation {
&self.location
}
pub fn error(&self) -> &E {
&self.error
}
pub fn set_error<F: std::error::Error>(self, error: F) -> SourceError<F> {
SourceError {
error,
message: None,
location: self.location,
}
}
pub fn set_location(mut self, location: impl SourceLoc) -> Self {
self.location = location.source_location();
self
}
pub fn add_source_info(mut self, info: SourceInfo) -> Self {
self.message = Some(Self::create_message(
info.0,
&self.location,
&format!("{}", self.error),
));
self
}
fn create_message(parser: &TableGenParser, location: &SourceLocation, message: &str) -> String {
let mut data: (_, Result<_, TableGenError>) = (String::new(), Ok(()));
let res = unsafe {
tableGenPrintError(
parser.raw,
location.raw,
TABLEGEN_DK_ERROR,
StringRef::from(message).to_raw(),
Some(print_string_callback),
&mut data as *mut _ as *mut c_void,
)
};
if res == 0 {
data.1 = Err(TableGenError::InvalidSourceLocation);
}
if let Err(e) = data.1 {
data.0 = format!("{}\nfailed to print source information: {}", message, e);
}
data.0
}
}
impl<E: std::error::Error> Display for SourceError<E> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if let Some(message) = self.message.as_ref() {
write!(f, "{}", message)
} else {
write!(f, "{}", self.error)
}
}
}
impl<E: std::error::Error + 'static> std::error::Error for SourceError<E> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.error)
}
}
impl From<TableGenError> for SourceError<TableGenError> {
fn from(value: TableGenError) -> Self {
value.with_location(SourceLocation::none())
}
}
pub trait WithLocation: std::error::Error + Sized {
fn with_location<L: SourceLoc>(self, location: L) -> SourceError<Self> {
SourceError::new(location.source_location(), self)
}
}
impl<E> WithLocation for E where E: std::error::Error {}
pub trait SourceLoc {
fn source_location(self) -> SourceLocation;
}
impl SourceLoc for SourceLocation {
fn source_location(self) -> SourceLocation {
self
}
}
pub type Error = SourceError<TableGenError>;