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    ExpectedLine(Span),
19}
20
21impl HasSpan for ParseError {
22    fn span(&self) -> Span {
23        match self {
24            ParseError::InvalidTrivia(span)
25            | ParseError::UnclosedInlineTag(span)
26            | ParseError::UnclosedInlineCode(span)
27            | ParseError::UnclosedCodeBlock(span)
28            | ParseError::InvalidTagName(span)
29            | ParseError::InvalidAnnotationName(span)
30            | ParseError::UnclosedAnnotationArguments(span)
31            | ParseError::MalformedCodeBlock(span)
32            | ParseError::InvalidComment(span)
33            | ParseError::ExpectedLine(span) => *span,
34        }
35    }
36}
37
38impl std::error::Error for ParseError {}
39
40impl std::fmt::Display for ParseError {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        match self {
43            ParseError::InvalidTrivia(_) | ParseError::InvalidComment(_) => {
44                write!(f, "Invalid docblock format")
45            }
46            ParseError::UnclosedInlineTag(_) => write!(f, "Unclosed inline tag"),
47            ParseError::UnclosedInlineCode(_) => write!(f, "Unclosed inline code"),
48            ParseError::UnclosedCodeBlock(_) => write!(f, "Unclosed code block"),
49            ParseError::InvalidTagName(_) => write!(f, "Invalid tag name"),
50            ParseError::InvalidAnnotationName(_) => write!(f, "Invalid annotation name"),
51            ParseError::UnclosedAnnotationArguments(_) => write!(f, "Unclosed annotation arguments"),
52            ParseError::MalformedCodeBlock(_) => write!(f, "Malformed code block"),
53            ParseError::ExpectedLine(_) => write!(f, "Unexpected end of docblock"),
54        }
55    }
56}
57
58impl ParseError {
59    pub fn note(&self) -> String {
60        match self {
61            ParseError::InvalidTrivia(_) | ParseError::InvalidComment(_) => {
62                "Docblocks must start with `/**` and end with `*/`.".to_string()
63            }
64            ParseError::UnclosedInlineTag(_) => {
65                "Inline tags like `{@see}` must be closed with a matching `}`.".to_string()
66            }
67            ParseError::UnclosedInlineCode(_) => {
68                "Inline code snippets must be enclosed in matching backticks (`).".to_string()
69            }
70            ParseError::UnclosedCodeBlock(_) => {
71                "Multi-line code blocks must be terminated with a closing ```.".to_string()
72            }
73            ParseError::InvalidTagName(_) => {
74                "Docblock tags like `@param` must contain only letters, numbers, hyphens, and colons.".to_string()
75            }
76            ParseError::InvalidAnnotationName(_) => {
77                "Annotations must start with an uppercase letter, `_`, or `\\`.".to_string()
78            }
79            ParseError::UnclosedAnnotationArguments(_) => {
80                "Arguments for an annotation must be enclosed in parentheses `()`.".to_string()
81            }
82            ParseError::MalformedCodeBlock(_) => {
83                "A code block must start with ``` optionally followed by a language identifier.".to_string()
84            }
85            ParseError::ExpectedLine(_) => {
86                "A tag or description was expected here, but the docblock ended prematurely.".to_string()
87            }
88        }
89    }
90
91    pub fn help(&self) -> String {
92        match self {
93            ParseError::UnclosedInlineTag(_) => "Add a closing `}` to complete the inline tag.".to_string(),
94            ParseError::UnclosedInlineCode(_) => {
95                "Add a closing backtick ` ` ` to terminate the inline code.".to_string()
96            }
97            ParseError::UnclosedCodeBlock(_) => "Add a closing ``` to terminate the code block.".to_string(),
98            ParseError::InvalidTagName(_) => {
99                "Correct the tag name to use only valid characters (e.g., `@my-custom-tag`).".to_string()
100            }
101            ParseError::InvalidAnnotationName(_) => {
102                "Correct the annotation name to follow PSR-5 standards.".to_string()
103            }
104            ParseError::UnclosedAnnotationArguments(_) => {
105                "Add a closing `)` to complete the annotation's argument list.".to_string()
106            }
107            _ => "Review the docblock syntax to ensure it is correctly formatted.".to_string(),
108        }
109    }
110}