use alloc::string::String;
use core::fmt;
use core::num::NonZeroUsize;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error {
Parse {
message: String,
position: usize,
},
Serialize {
message: String,
},
Validate {
message: String,
},
}
impl Error {
pub fn position(&self) -> Option<usize> {
match self {
Error::Parse { position, .. } => Some(*position),
Error::Serialize { .. } | Error::Validate { .. } => None,
}
}
pub fn get_line_and_column(&self, text: &str) -> Option<(NonZeroUsize, NonZeroUsize)> {
let position = self.position()?;
let position = position.min(text.len());
let mut line = 1usize;
let mut col = 1usize;
for (i, ch) in text.char_indices() {
if i >= position {
break;
}
if ch == '\n' {
line += 1;
col = 1;
} else {
col += 1;
}
}
Some((NonZeroUsize::new(line)?, NonZeroUsize::new(col)?))
}
pub fn get_line<'a>(&self, text: &'a str) -> Option<&'a str> {
let position = self.position()?;
let position = position.min(text.len());
let start = text[..position].rfind('\n').map_or(0, |i| i + 1);
let end = text[position..]
.find('\n')
.map_or(text.len(), |i| position + i);
Some(&text[start..end])
}
pub(crate) fn parse(position: usize, message: impl Into<String>) -> Self {
Error::Parse {
message: message.into(),
position,
}
}
pub(crate) fn serialize(message: impl Into<String>) -> Self {
Error::Serialize {
message: message.into(),
}
}
pub(crate) fn validate(message: impl Into<String>) -> Self {
Error::Validate {
message: message.into(),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Parse { message, position } => {
write!(f, "parse error at byte {position}: {message}")
}
Error::Serialize { message } => {
write!(f, "serialize error: {message}")
}
Error::Validate { message } => {
write!(f, "validation error: {message}")
}
}
}
}
impl core::error::Error for Error {}