use crate::source::SourceId;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
mod display;
mod from_std;
mod source;
pub type LexResult<T> = Result<T, OakError>;
pub type ParseResult<T> = Result<T, OakError>;
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>")))]
pub struct OakDiagnostics<T> {
pub result: Result<T, OakError>,
pub diagnostics: Vec<OakError>,
}
impl<T: Clone> Clone for OakDiagnostics<T> {
fn clone(&self) -> Self {
Self { result: self.result.clone(), diagnostics: self.diagnostics.clone() }
}
}
impl<T> OakDiagnostics<T> {
pub fn new(result: Result<T, OakError>) -> Self {
Self { result, diagnostics: Vec::new() }
}
pub fn success(value: T) -> Self {
Self { result: Ok(value), diagnostics: Vec::new() }
}
pub fn error(error: OakError) -> Self {
Self { result: Err(error), diagnostics: Vec::new() }
}
pub fn has_errors(&self) -> bool {
self.result.is_err() || !self.diagnostics.is_empty()
}
}
impl<'a, L: crate::Language> OakDiagnostics<&'a crate::tree::GreenNode<'a, L>> {
pub fn green(&self) -> &'a crate::tree::GreenNode<'a, L> {
self.result.as_ref().expect("Failed to get green node from parse output")
}
}
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct OakError {
kind: Box<OakErrorKind>,
}
impl OakError {
pub fn new(kind: OakErrorKind) -> Self {
Self { kind: Box::new(kind) }
}
pub fn custom_error(message: impl Into<String>) -> Self {
Self::new(OakErrorKind::CustomError { message: message.into() })
}
}
impl std::fmt::Debug for OakError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self, f)
}
}
#[cfg(feature = "serde")]
impl serde::ser::Error for OakError {
fn custom<T: std::fmt::Display>(msg: T) -> Self {
OakError::serde_error(msg.to_string())
}
}
#[cfg(feature = "serde")]
impl serde::de::Error for OakError {
fn custom<T: std::fmt::Display>(msg: T) -> Self {
OakError::deserialize_error(msg.to_string())
}
}
#[cfg(feature = "serde")]
mod serde_io_error {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S>(error: &std::io::Error, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
error.to_string().serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<std::io::Error, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(std::io::Error::new(std::io::ErrorKind::Other, s))
}
}
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum OakErrorKind {
IoError {
#[cfg_attr(feature = "serde", serde(with = "crate::errors::serde_io_error"))]
error: std::io::Error,
source_id: Option<SourceId>,
},
SyntaxError {
message: String,
offset: usize,
source_id: Option<SourceId>,
},
UnexpectedCharacter {
character: char,
offset: usize,
source_id: Option<SourceId>,
},
UnexpectedToken {
token: String,
offset: usize,
source_id: Option<SourceId>,
},
UnexpectedEof {
offset: usize,
source_id: Option<SourceId>,
},
CustomError {
message: String,
},
InvalidTheme {
message: String,
},
UnsupportedFormat {
format: String,
},
ColorParseError {
color: String,
},
FormatError {
message: String,
},
SemanticError {
message: String,
},
ProtocolError {
message: String,
},
ExpectedToken {
expected: String,
offset: usize,
source_id: Option<SourceId>,
},
ExpectedName {
name_kind: String,
offset: usize,
source_id: Option<SourceId>,
},
TrailingCommaNotAllowed {
offset: usize,
source_id: Option<SourceId>,
},
TestFailure {
path: std::path::PathBuf,
expected: String,
actual: String,
},
TestRegenerated {
path: std::path::PathBuf,
},
SerdeError {
message: String,
},
DeserializeError {
message: String,
},
XmlError {
message: String,
},
ZipError {
message: String,
},
ParseError {
message: String,
},
InternalError {
message: String,
},
}
impl OakErrorKind {
pub fn key(&self) -> &'static str {
match self {
OakErrorKind::IoError { .. } => "error.io",
OakErrorKind::SyntaxError { .. } => "error.syntax",
OakErrorKind::UnexpectedCharacter { .. } => "error.unexpected_character",
OakErrorKind::UnexpectedToken { .. } => "error.unexpected_token",
OakErrorKind::UnexpectedEof { .. } => "error.unexpected_eof",
OakErrorKind::CustomError { .. } => "error.custom",
OakErrorKind::InvalidTheme { .. } => "error.invalid_theme",
OakErrorKind::UnsupportedFormat { .. } => "error.unsupported_format",
OakErrorKind::ColorParseError { .. } => "error.color_parse",
OakErrorKind::FormatError { .. } => "error.format",
OakErrorKind::SemanticError { .. } => "error.semantic",
OakErrorKind::ProtocolError { .. } => "error.protocol",
OakErrorKind::ExpectedToken { .. } => "error.expected_token",
OakErrorKind::ExpectedName { .. } => "error.expected_name",
OakErrorKind::TrailingCommaNotAllowed { .. } => "error.trailing_comma_not_allowed",
OakErrorKind::TestFailure { .. } => "error.test_failure",
OakErrorKind::TestRegenerated { .. } => "error.test_regenerated",
OakErrorKind::SerdeError { .. } => "error.serde",
OakErrorKind::DeserializeError { .. } => "error.deserialize",
OakErrorKind::XmlError { .. } => "error.xml",
OakErrorKind::ZipError { .. } => "error.zip",
OakErrorKind::ParseError { .. } => "error.parse",
OakErrorKind::InternalError { .. } => "error.internal",
}
}
}
impl OakError {
pub fn kind(&self) -> &OakErrorKind {
&self.kind
}
pub fn test_failure(path: std::path::PathBuf, expected: String, actual: String) -> Self {
OakErrorKind::TestFailure { path, expected, actual }.into()
}
pub fn test_regenerated(path: std::path::PathBuf) -> Self {
OakErrorKind::TestRegenerated { path }.into()
}
pub fn io_error(error: std::io::Error, source_id: SourceId) -> Self {
OakErrorKind::IoError { error, source_id: Some(source_id) }.into()
}
pub fn syntax_error(message: impl Into<String>, offset: usize, source_id: Option<SourceId>) -> Self {
OakErrorKind::SyntaxError { message: message.into(), offset, source_id }.into()
}
pub fn unexpected_character(character: char, offset: usize, source_id: Option<SourceId>) -> Self {
OakErrorKind::UnexpectedCharacter { character, offset, source_id }.into()
}
pub fn unexpected_token(token: impl Into<String>, offset: usize, source_id: Option<SourceId>) -> Self {
OakErrorKind::UnexpectedToken { token: token.into(), offset, source_id }.into()
}
pub fn unexpected_eof(offset: usize, source_id: Option<SourceId>) -> Self {
OakErrorKind::UnexpectedEof { offset, source_id }.into()
}
pub fn expected_token(expected: impl Into<String>, offset: usize, source_id: Option<SourceId>) -> Self {
OakErrorKind::ExpectedToken { expected: expected.into(), offset, source_id }.into()
}
pub fn expected_name(name_kind: impl Into<String>, offset: usize, source_id: Option<SourceId>) -> Self {
OakErrorKind::ExpectedName { name_kind: name_kind.into(), offset, source_id }.into()
}
pub fn trailing_comma_not_allowed(offset: usize, source_id: Option<SourceId>) -> Self {
OakErrorKind::TrailingCommaNotAllowed { offset, source_id }.into()
}
pub fn invalid_theme(message: impl Into<String>) -> Self {
OakErrorKind::InvalidTheme { message: message.into() }.into()
}
pub fn unsupported_format(format: impl Into<String>) -> Self {
OakErrorKind::UnsupportedFormat { format: format.into() }.into()
}
pub fn color_parse_error(color: impl Into<String>) -> Self {
OakErrorKind::ColorParseError { color: color.into() }.into()
}
pub fn format_error(message: impl Into<String>) -> Self {
OakErrorKind::FormatError { message: message.into() }.into()
}
pub fn semantic_error(message: impl Into<String>) -> Self {
OakErrorKind::SemanticError { message: message.into() }.into()
}
pub fn protocol_error(message: impl Into<String>) -> Self {
OakErrorKind::ProtocolError { message: message.into() }.into()
}
pub fn serde_error(message: impl Into<String>) -> Self {
OakErrorKind::SerdeError { message: message.into() }.into()
}
pub fn deserialize_error(message: impl Into<String>) -> Self {
OakErrorKind::DeserializeError { message: message.into() }.into()
}
pub fn xml_error(message: impl Into<String>) -> Self {
OakErrorKind::XmlError { message: message.into() }.into()
}
pub fn zip_error(message: impl Into<String>) -> Self {
OakErrorKind::ZipError { message: message.into() }.into()
}
pub fn parse_error(message: impl Into<String>) -> Self {
OakErrorKind::ParseError { message: message.into() }.into()
}
pub fn internal_error(message: impl Into<String>) -> Self {
OakErrorKind::InternalError { message: message.into() }.into()
}
pub fn with_source_id(mut self, source_id: SourceId) -> Self {
match self.kind.as_mut() {
OakErrorKind::IoError { source_id: u, .. } => *u = Some(source_id),
OakErrorKind::SyntaxError { source_id: u, .. } => *u = Some(source_id),
OakErrorKind::UnexpectedCharacter { source_id: u, .. } => *u = Some(source_id),
OakErrorKind::UnexpectedToken { source_id: u, .. } => *u = Some(source_id),
OakErrorKind::ExpectedToken { source_id: u, .. } => *u = Some(source_id),
OakErrorKind::ExpectedName { source_id: u, .. } => *u = Some(source_id),
OakErrorKind::TrailingCommaNotAllowed { source_id: u, .. } => *u = Some(source_id),
_ => {}
}
self
}
}
impl Clone for OakErrorKind {
fn clone(&self) -> Self {
match self {
OakErrorKind::IoError { error, source_id } => {
let new_error = std::io::Error::new(error.kind(), error.to_string());
OakErrorKind::IoError { error: new_error, source_id: *source_id }
}
OakErrorKind::SyntaxError { message, offset, source_id } => OakErrorKind::SyntaxError { message: message.clone(), offset: *offset, source_id: *source_id },
OakErrorKind::UnexpectedCharacter { character, offset, source_id } => OakErrorKind::UnexpectedCharacter { character: *character, offset: *offset, source_id: *source_id },
OakErrorKind::UnexpectedToken { token, offset, source_id } => OakErrorKind::UnexpectedToken { token: token.clone(), offset: *offset, source_id: *source_id },
OakErrorKind::UnexpectedEof { offset, source_id } => OakErrorKind::UnexpectedEof { offset: *offset, source_id: *source_id },
OakErrorKind::ExpectedToken { expected, offset, source_id } => OakErrorKind::ExpectedToken { expected: expected.clone(), offset: *offset, source_id: *source_id },
OakErrorKind::ExpectedName { name_kind, offset, source_id } => OakErrorKind::ExpectedName { name_kind: name_kind.clone(), offset: *offset, source_id: *source_id },
OakErrorKind::TrailingCommaNotAllowed { offset, source_id } => OakErrorKind::TrailingCommaNotAllowed { offset: *offset, source_id: *source_id },
OakErrorKind::CustomError { message } => OakErrorKind::CustomError { message: message.clone() },
OakErrorKind::InvalidTheme { message } => OakErrorKind::InvalidTheme { message: message.clone() },
OakErrorKind::UnsupportedFormat { format } => OakErrorKind::UnsupportedFormat { format: format.clone() },
OakErrorKind::ColorParseError { color } => OakErrorKind::ColorParseError { color: color.clone() },
OakErrorKind::FormatError { message } => OakErrorKind::FormatError { message: message.clone() },
OakErrorKind::SemanticError { message } => OakErrorKind::SemanticError { message: message.clone() },
OakErrorKind::ProtocolError { message } => OakErrorKind::ProtocolError { message: message.clone() },
OakErrorKind::TestFailure { path, expected, actual } => OakErrorKind::TestFailure { path: path.clone(), expected: expected.clone(), actual: actual.clone() },
OakErrorKind::TestRegenerated { path } => OakErrorKind::TestRegenerated { path: path.clone() },
OakErrorKind::SerdeError { message } => OakErrorKind::SerdeError { message: message.clone() },
OakErrorKind::DeserializeError { message } => OakErrorKind::DeserializeError { message: message.clone() },
OakErrorKind::XmlError { message } => OakErrorKind::XmlError { message: message.clone() },
OakErrorKind::ZipError { message } => OakErrorKind::ZipError { message: message.clone() },
OakErrorKind::ParseError { message } => OakErrorKind::ParseError { message: message.clone() },
OakErrorKind::InternalError { message } => OakErrorKind::InternalError { message: message.clone() },
}
}
}