use crate::source::SourceId;
pub type ParseResult<T> = Result<T, OakError>;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(bound(serialize = "T: serde::Serialize", deserialize = "T: serde::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(serde::Serialize, serde::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 From<OakErrorKind> for OakError {
fn from(kind: OakErrorKind) -> Self {
Self { kind: Box::new(kind) }
}
}
impl std::fmt::Debug for OakError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self, f)
}
}
impl std::fmt::Display for OakError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.kind)
}
}
impl std::error::Error for OakError {}
#[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 {
pub fn serialize<S>(error: &std::io::Error, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serde::Serialize::serialize(&error.to_string(), serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<std::io::Error, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = <String as serde::Deserialize>::deserialize(deserializer)?;
Ok(std::io::Error::new(std::io::ErrorKind::Other, s))
}
}
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::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 std::fmt::Display for OakErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
OakErrorKind::IoError { error, source_id } => {
if let Some(id) = source_id {
write!(f, "I/O error in {}: {}", id, error)
}
else {
write!(f, "I/O error: {}", error)
}
}
OakErrorKind::SyntaxError { message, offset, source_id } => {
if let Some(id) = source_id {
write!(f, "Syntax error in {} at offset {}: {}", id, offset, message)
}
else {
write!(f, "Syntax error at offset {}: {}", offset, message)
}
}
OakErrorKind::UnexpectedCharacter { character, offset, source_id } => {
if let Some(id) = source_id {
write!(f, "Unexpected character '{}' in {} at offset {}", character, id, offset)
}
else {
write!(f, "Unexpected character '{}' at offset {}", character, offset)
}
}
OakErrorKind::UnexpectedToken { token, offset, source_id } => {
if let Some(id) = source_id {
write!(f, "Unexpected token '{}' in {} at offset {}", token, id, offset)
}
else {
write!(f, "Unexpected token '{}' at offset {}", token, offset)
}
}
OakErrorKind::UnexpectedEof { offset, source_id } => {
if let Some(id) = source_id {
write!(f, "Unexpected end of file in {} at offset {}", id, offset)
}
else {
write!(f, "Unexpected end of file at offset {}", offset)
}
}
OakErrorKind::ExpectedToken { expected, offset, source_id } => {
if let Some(id) = source_id {
write!(f, "Expected token '{}' in {} at offset {}", expected, id, offset)
}
else {
write!(f, "Expected token '{}' at offset {}", expected, offset)
}
}
OakErrorKind::ExpectedName { name_kind, offset, source_id } => {
if let Some(id) = source_id {
write!(f, "Expected {} in {} at offset {}", name_kind, id, offset)
}
else {
write!(f, "Expected {} at offset {}", name_kind, offset)
}
}
OakErrorKind::TrailingCommaNotAllowed { offset, source_id } => {
if let Some(id) = source_id {
write!(f, "Trailing comma not allowed in {} at offset {}", id, offset)
}
else {
write!(f, "Trailing comma not allowed at offset {}", offset)
}
}
OakErrorKind::CustomError { message } => {
write!(f, "Custom error: {}", message)
}
OakErrorKind::InvalidTheme { message } => {
write!(f, "Invalid theme: {}", message)
}
OakErrorKind::UnsupportedFormat { format } => {
write!(f, "Unsupported format: {}", format)
}
OakErrorKind::ColorParseError { color } => {
write!(f, "Invalid color: {}", color)
}
OakErrorKind::FormatError { message } => {
write!(f, "Format error: {}", message)
}
OakErrorKind::SemanticError { message } => {
write!(f, "Semantic error: {}", message)
}
OakErrorKind::ProtocolError { message } => {
write!(f, "Protocol error: {}", message)
}
OakErrorKind::TestFailure { path, expected, actual } => {
write!(f, "Test failed for {}: expected '{}', got '{}'", path.display(), expected, actual)
}
OakErrorKind::TestRegenerated { path } => {
write!(f, "Test regenerated for {}", path.display())
}
OakErrorKind::SerdeError { message } => {
write!(f, "Serialization error: {}", message)
}
OakErrorKind::DeserializeError { message } => {
write!(f, "Deserialization error: {}", message)
}
OakErrorKind::XmlError { message } => {
write!(f, "XML error: {}", message)
}
OakErrorKind::ZipError { message } => {
write!(f, "ZIP error: {}", message)
}
OakErrorKind::ParseError { message } => {
write!(f, "Parse error: {}", message)
}
OakErrorKind::InternalError { message } => {
write!(f, "Internal error: {}", message)
}
}
}
}
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() },
}
}
}