version_number/parsers/original/
error.rs1use super::*;
2use crate::parsers::error::ExpectedError;
3use crate::parsers::NumericError;
4
5#[derive(Clone, Debug, thiserror::Error)]
7#[error(
8 "Unable to parse '{input}' to a version number: {reason}{}",
9 self.fmt()
10)]
11pub struct OriginalParserError {
12 input: String,
13 cursor: Option<usize>,
14 reason: ErrorReason,
15}
16
17impl OriginalParserError {
18 pub fn reason(&self) -> &ErrorReason {
20 &self.reason
21 }
22}
23
24impl OriginalParserError {
25 pub(crate) fn from_parser(parser: &Parser<'_>, reason: ErrorReason) -> Self {
26 Self {
27 input: String::from_utf8_lossy(parser.slice).to_string(),
28 cursor: None,
29 reason,
30 }
31 }
32
33 pub(crate) fn from_parser_with_cursor(
34 slice: &Parser<'_>,
35 cursor: usize,
36 reason: ErrorReason,
37 ) -> Self {
38 Self {
39 input: String::from_utf8_lossy(slice.slice).to_string(),
40 cursor: Some(cursor),
41 reason,
42 }
43 }
44
45 fn fmt(&self) -> String {
46 if let Some(c) = self.cursor {
47 Self::squiggle(&self.input, c).unwrap_or_default()
48 } else {
49 String::default()
50 }
51 }
52
53 fn squiggle(input: &str, cursor: usize) -> Option<String> {
54 let lead = "Unable to parse '".len();
55 let err_from = lead + cursor;
56 let err_end = input.len().checked_sub(cursor + 1)?; let spaces = std::iter::repeat_with(|| " ").take(err_from);
59 let marker = std::iter::once_with(|| "^");
60 let squiggle = std::iter::repeat_with(|| "~").take(err_end);
61 let newline = std::iter::once_with(|| "\n");
62
63 Some(
64 newline
65 .clone()
66 .chain(spaces)
67 .chain(marker)
68 .chain(squiggle)
69 .chain(newline)
70 .collect(),
71 )
72 }
73}
74
75#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
77pub enum ErrorReason {
78 #[error("Expected end of input after parsing third version number component, but got: '{}'", String::from_utf8_lossy(.extra_input.as_slice()))]
86 ExpectedEndOfInput {
87 extra_input: Vec<u8>,
90 },
91
92 #[error(
97 "Expected the dot-separator '.', but got '{}'",
98 .got.map(|c| String::from(char::from(c))).unwrap_or_else(|| "EOI".to_string()),
99 )]
100 ExpectedSeparator {
101 got: Option<u8>,
103 },
104
105 #[error(
108 "Expected 0-9, but got '{}'",
109 .got.map(|c| String::from(char::from(c))).unwrap_or_else(|| "EOI".to_string()),
110 )]
111 ExpectedNumericToken {
112 got: Option<u8>,
114 },
115
116 #[error(transparent)]
118 NumberError(#[from] NumberError),
119}
120
121#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
123pub enum NumberError {
124 #[error("Number may not start with a leading zero, unless the complete component is '0'")]
127 LeadingZero,
128
129 #[error("Overflow: Found number component which would be larger than the maximum supported number (max={})", u64::MAX)]
133 Overflow,
134}
135
136impl From<OriginalParserError> for ParserError {
137 fn from(value: OriginalParserError) -> Self {
138 match value.reason {
139 ErrorReason::NumberError(e) => match e {
140 NumberError::LeadingZero => ParserError::Numeric(NumericError::LeadingZero),
141 NumberError::Overflow => ParserError::Numeric(NumericError::Overflow),
142 },
143 ErrorReason::ExpectedEndOfInput { extra_input } => {
144 ParserError::Expected(ExpectedError::EndOfInput {
145 at: value.cursor,
146 got: char::from(extra_input[0]),
147 })
148 }
149 ErrorReason::ExpectedSeparator { got } => {
150 ParserError::Expected(ExpectedError::Separator {
151 at: value.cursor,
152 got: got.map(char::from),
153 })
154 }
155 ErrorReason::ExpectedNumericToken { got } => {
156 ParserError::Expected(ExpectedError::Numeric {
157 at: value.cursor,
158 got: got.map(char::from),
159 })
160 }
161 }
162 }
163}