mago_docblock/
error.rs

1use serde::Deserialize;
2use serde::Serialize;
3
4use mago_span::HasSpan;
5use mago_span::Span;
6
7#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
8pub enum ParseError {
9    InvalidTrivia(Span),
10    UnclosedInlineTag(Span),
11    UnclosedInlineCode(Span),
12    UnclosedCodeBlock(Span),
13    InvalidTagName(Span),
14    InvalidAnnotationName(Span),
15    UnclosedAnnotationArguments(Span),
16    MalformedCodeBlock(Span),
17    InvalidComment(Span),
18    InconsistentIndentation(Span, usize, usize),
19    MissingAsterisk(Span),
20    MissingWhitespaceAfterAsterisk(Span),
21    MissingWhitespaceAfterOpeningAsterisk(Span),
22    MissingWhitespaceBeforeClosingAsterisk(Span),
23    ExpectedLine(Span),
24}
25
26impl HasSpan for ParseError {
27    fn span(&self) -> Span {
28        match self {
29            ParseError::InvalidTrivia(span) => *span,
30            ParseError::UnclosedInlineTag(span) => *span,
31            ParseError::UnclosedInlineCode(span) => *span,
32            ParseError::UnclosedCodeBlock(span) => *span,
33            ParseError::InvalidTagName(span) => *span,
34            ParseError::InvalidAnnotationName(span) => *span,
35            ParseError::UnclosedAnnotationArguments(span) => *span,
36            ParseError::MalformedCodeBlock(span) => *span,
37            ParseError::InvalidComment(span) => *span,
38            ParseError::InconsistentIndentation(span, _, _) => *span,
39            ParseError::MissingAsterisk(span) => *span,
40            ParseError::MissingWhitespaceAfterAsterisk(span) => *span,
41            ParseError::MissingWhitespaceAfterOpeningAsterisk(span) => *span,
42            ParseError::MissingWhitespaceBeforeClosingAsterisk(span) => *span,
43            ParseError::ExpectedLine(span) => *span,
44        }
45    }
46}
47
48impl std::error::Error for ParseError {}
49
50impl std::fmt::Display for ParseError {
51    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52        match self {
53            ParseError::InvalidTrivia(_) => {
54                write!(f, "Invalid docblock comment.")
55            }
56            ParseError::UnclosedInlineTag(_) => {
57                write!(f, "Unclosed inline tag.")
58            }
59            ParseError::UnclosedInlineCode(_) => {
60                write!(f, "Unclosed inline code.")
61            }
62            ParseError::UnclosedCodeBlock(_) => {
63                write!(f, "Unclosed code block.")
64            }
65            ParseError::InvalidTagName(_) => {
66                write!(f, "Invalid tag name.")
67            }
68            ParseError::InvalidAnnotationName(_) => {
69                write!(f, "Invalid annotation name.")
70            }
71            ParseError::UnclosedAnnotationArguments(_) => {
72                write!(f, "Unclosed annotation arguments.")
73            }
74            ParseError::MalformedCodeBlock(_) => {
75                write!(f, "Malformed code block.")
76            }
77            ParseError::InvalidComment(_) => {
78                write!(f, "Invalid comment.")
79            }
80            ParseError::InconsistentIndentation(_, _, _) => {
81                write!(f, "Inconsistent indentation.")
82            }
83            ParseError::MissingAsterisk(_) => {
84                write!(f, "Missing `*` in a docblock comment.")
85            }
86            ParseError::MissingWhitespaceAfterAsterisk(_) => {
87                write!(f, "Missing whitespace after `*`.")
88            }
89            ParseError::MissingWhitespaceAfterOpeningAsterisk(_) => {
90                write!(f, "Missing whitespace after `/*` in a single-line docblock.")
91            }
92            ParseError::MissingWhitespaceBeforeClosingAsterisk(_) => {
93                write!(f, "Missing whitespace before `*/` in a single-line docblock.")
94            }
95            ParseError::ExpectedLine(_) => {
96                write!(f, "Missing expected line.")
97            }
98        }
99    }
100}
101
102impl ParseError {
103    pub fn note(&self) -> &'static str {
104        match self {
105            ParseError::InvalidTrivia(_) => {
106                "The comment is not recognized as a docblock. It should start with '/**' and end with '*/'."
107            }
108            ParseError::UnclosedInlineTag(_) => "The inline tag is missing a closing '}'.",
109            ParseError::UnclosedInlineCode(_) => "Inline code is missing a closing backtick '`'.",
110            ParseError::UnclosedCodeBlock(_) => "The code block is missing a closing delimiter '```'.",
111            ParseError::InvalidTagName(_) => "The tag name contains invalid characters.",
112            ParseError::InvalidAnnotationName(_) => {
113                "The annotation name is invalid. It must start with an uppercase letter, '_', or '\\', and contain only allowed characters."
114            }
115            ParseError::UnclosedAnnotationArguments(_) => {
116                "The annotation arguments are missing a closing parenthesis ')'."
117            }
118            ParseError::MalformedCodeBlock(_) => "The code block is malformed or incorrectly formatted.",
119            ParseError::InvalidComment(_) => {
120                "The comment is not a valid docblock. It should start with '/**' and end with '*/'."
121            }
122            ParseError::InconsistentIndentation(_, _, _) => "The indentation in the docblock comment is inconsistent.",
123            ParseError::MissingAsterisk(_) => "An asterisk '*' is missing at the beginning of a line in the docblock.",
124            ParseError::MissingWhitespaceAfterAsterisk(_) => {
125                "Missing whitespace after the asterisk '*' in the docblock."
126            }
127            ParseError::MissingWhitespaceAfterOpeningAsterisk(_) => {
128                "Missing whitespace after the opening '/**' in a single-line docblock."
129            }
130            ParseError::MissingWhitespaceBeforeClosingAsterisk(_) => {
131                "Missing whitespace before the closing '*/' in a single-line docblock."
132            }
133            ParseError::ExpectedLine(_) => "A line or tag was expected in the docblock but none was found.",
134        }
135    }
136
137    pub fn help(&self) -> &'static str {
138        match self {
139            ParseError::InvalidTrivia(_) => {
140                "Replace the comment with a proper docblock starting with '/**' and ending with '*/'."
141            }
142            ParseError::UnclosedInlineTag(_) => "Add a closing '}' to complete the inline tag.",
143            ParseError::UnclosedInlineCode(_) => "Add a closing '`' to terminate the inline code.",
144            ParseError::UnclosedCodeBlock(_) => "Add a closing '```' to terminate the code block.",
145            ParseError::InvalidTagName(_) => "Use only letters, numbers, and hyphens in the tag name.",
146            ParseError::InvalidAnnotationName(_) => {
147                "Correct the annotation name to start with an uppercase letter, '_', or '\\', and use only letters, numbers, '_', '\\', or unicode characters."
148            }
149            ParseError::UnclosedAnnotationArguments(_) => "Add a closing ')' to complete the annotation arguments.",
150            ParseError::MalformedCodeBlock(_) => {
151                "Ensure the code block starts with '```', optionally followed by a language identifier, and ends with a closing '```'."
152            }
153            ParseError::InvalidComment(_) => {
154                "Replace the comment with a valid docblock starting with '/**' and ending with '*/'."
155            }
156            ParseError::InconsistentIndentation(_, _, _) => {
157                "Adjust the indentation to be consistent across all lines in the docblock."
158            }
159            ParseError::MissingAsterisk(_) => {
160                "Add an '*' at the beginning of each line in the docblock after the opening '/**'."
161            }
162            ParseError::MissingWhitespaceAfterAsterisk(_) => {
163                "Insert a space after the '*' at the beginning of the line."
164            }
165            ParseError::MissingWhitespaceAfterOpeningAsterisk(_) => {
166                "Insert a space between '/**' and the text in the single-line docblock."
167            }
168            ParseError::MissingWhitespaceBeforeClosingAsterisk(_) => {
169                "Insert a space between the text and '*/' in the single-line docblock."
170            }
171            ParseError::ExpectedLine(_) => "Ensure that the docblock contains at least one line of text or a tag.",
172        }
173    }
174}