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(_) => "The comment is not recognized as a docblock. It should start with '/**' and end with '*/'.",
106 ParseError::UnclosedInlineTag(_) => "The inline tag is missing a closing '}'.",
107 ParseError::UnclosedInlineCode(_) => "Inline code is missing a closing backtick '`'.",
108 ParseError::UnclosedCodeBlock(_) => "The code block is missing a closing delimiter '```'.",
109 ParseError::InvalidTagName(_) => "The tag name contains invalid characters.",
110 ParseError::InvalidAnnotationName(_) => "The annotation name is invalid. It must start with an uppercase letter, '_', or '\\', and contain only allowed characters.",
111 ParseError::UnclosedAnnotationArguments(_) => "The annotation arguments are missing a closing parenthesis ')'.",
112 ParseError::MalformedCodeBlock(_) => "The code block is malformed or incorrectly formatted.",
113 ParseError::InvalidComment(_) => "The comment is not a valid docblock. It should start with '/**' and end with '*/'.",
114 ParseError::InconsistentIndentation(_, _, _) => "The indentation in the docblock comment is inconsistent.",
115 ParseError::MissingAsterisk(_) => "An asterisk '*' is missing at the beginning of a line in the docblock.",
116 ParseError::MissingWhitespaceAfterAsterisk(_) => "Missing whitespace after the asterisk '*' in the docblock.",
117 ParseError::MissingWhitespaceAfterOpeningAsterisk(_) => "Missing whitespace after the opening '/**' in a single-line docblock.",
118 ParseError::MissingWhitespaceBeforeClosingAsterisk(_) => "Missing whitespace before the closing '*/' in a single-line docblock.",
119 ParseError::ExpectedLine(_) => "A line or tag was expected in the docblock but none was found.",
120 }
121 }
122
123 pub fn help(&self) -> &'static str {
124 match self {
125 ParseError::InvalidTrivia(_) => "Replace the comment with a proper docblock starting with '/**' and ending with '*/'.",
126 ParseError::UnclosedInlineTag(_) => "Add a closing '}' to complete the inline tag.",
127 ParseError::UnclosedInlineCode(_) => "Add a closing '`' to terminate the inline code.",
128 ParseError::UnclosedCodeBlock(_) => "Add a closing '```' to terminate the code block.",
129 ParseError::InvalidTagName(_) => "Use only letters, numbers, and hyphens in the tag name.",
130 ParseError::InvalidAnnotationName(_) => "Correct the annotation name to start with an uppercase letter, '_', or '\\', and use only letters, numbers, '_', '\\', or unicode characters.",
131 ParseError::UnclosedAnnotationArguments(_) => "Add a closing ')' to complete the annotation arguments.",
132 ParseError::MalformedCodeBlock(_) => "Ensure the code block starts with '```', optionally followed by a language identifier, and ends with a closing '```'.",
133 ParseError::InvalidComment(_) => "Replace the comment with a valid docblock starting with '/**' and ending with '*/'.",
134 ParseError::InconsistentIndentation(_, _, _) => "Adjust the indentation to be consistent across all lines in the docblock.",
135 ParseError::MissingAsterisk(_) => "Add an '*' at the beginning of each line in the docblock after the opening '/**'.",
136 ParseError::MissingWhitespaceAfterAsterisk(_) => "Insert a space after the '*' at the beginning of the line.",
137 ParseError::MissingWhitespaceAfterOpeningAsterisk(_) => "Insert a space between '/**' and the text in the single-line docblock.",
138 ParseError::MissingWhitespaceBeforeClosingAsterisk(_) => "Insert a space between the text and '*/' in the single-line docblock.",
139 ParseError::ExpectedLine(_) => "Ensure that the docblock contains at least one line of text or a tag.",
140 }
141 }
142}