use super::*;
use crate::parsers::error::ExpectedError;
use crate::parsers::NumericError;
#[derive(Clone, Debug, thiserror::Error)]
#[error(
"Unable to parse '{input}' to a version number: {reason}{}",
self.fmt()
)]
pub struct OriginalParserError {
input: String,
cursor: Option<usize>,
reason: ErrorReason,
}
impl OriginalParserError {
pub fn reason(&self) -> &ErrorReason {
&self.reason
}
}
impl OriginalParserError {
pub(crate) fn from_parser(parser: &Parser<'_>, reason: ErrorReason) -> Self {
Self {
input: String::from_utf8_lossy(parser.slice).to_string(),
cursor: None,
reason,
}
}
pub(crate) fn from_parser_with_cursor(
slice: &Parser<'_>,
cursor: usize,
reason: ErrorReason,
) -> Self {
Self {
input: String::from_utf8_lossy(slice.slice).to_string(),
cursor: Some(cursor),
reason,
}
}
fn fmt(&self) -> String {
if let Some(c) = self.cursor {
Self::squiggle(&self.input, c).unwrap_or_default()
} else {
String::default()
}
}
fn squiggle(input: &str, cursor: usize) -> Option<String> {
let lead = "Unable to parse '".len();
let err_from = lead + cursor;
let err_end = input.len().checked_sub(cursor + 1)?;
let spaces = std::iter::repeat_with(|| " ").take(err_from);
let marker = std::iter::once_with(|| "^");
let squiggle = std::iter::repeat_with(|| "~").take(err_end);
let newline = std::iter::once_with(|| "\n");
Some(
newline
.clone()
.chain(spaces)
.chain(marker)
.chain(squiggle)
.chain(newline)
.collect(),
)
}
}
#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
pub enum ErrorReason {
#[error("Expected end of input after parsing third version number component, but got: '{}'", String::from_utf8_lossy(.extra_input.as_slice()))]
ExpectedEndOfInput {
extra_input: Vec<u8>,
},
#[error(
"Expected the dot-separator '.', but got '{}'",
.got.map(|c| String::from(char::from(c))).unwrap_or_else(|| "EOI".to_string()),
)]
ExpectedSeparator {
got: Option<u8>,
},
#[error(
"Expected 0-9, but got '{}'",
.got.map(|c| String::from(char::from(c))).unwrap_or_else(|| "EOI".to_string()),
)]
ExpectedNumericToken {
got: Option<u8>,
},
#[error(transparent)]
NumberError(#[from] NumberError),
}
#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
pub enum NumberError {
#[error("Number may not start with a leading zero, unless the complete component is '0'")]
LeadingZero,
#[error("Overflow: Found number component which would be larger than the maximum supported number (max={})", u64::MAX)]
Overflow,
}
impl From<OriginalParserError> for ParserError {
fn from(value: OriginalParserError) -> Self {
match value.reason {
ErrorReason::NumberError(e) => match e {
NumberError::LeadingZero => ParserError::Numeric(NumericError::LeadingZero),
NumberError::Overflow => ParserError::Numeric(NumericError::Overflow),
},
ErrorReason::ExpectedEndOfInput { extra_input } => {
ParserError::Expected(ExpectedError::EndOfInput {
at: value.cursor,
got: char::from(extra_input[0]),
})
}
ErrorReason::ExpectedSeparator { got } => {
ParserError::Expected(ExpectedError::Separator {
at: value.cursor,
got: got.map(char::from),
})
}
ErrorReason::ExpectedNumericToken { got } => {
ParserError::Expected(ExpectedError::Numeric {
at: value.cursor,
got: got.map(char::from),
})
}
}
}
}