1 | use std::fmt; |
2 | |
3 | use proc_macro2::{Span, TokenStream as TokenStream2}; |
4 | use quote::ToTokens; |
5 | |
6 | /// An error with an optional span. Most errors will have a span, the only exception is on a |
7 | /// [`Error::Parse`], which can occur in the [`get_args_and_format_string()`] function. |
8 | /// |
9 | /// [`get_args_and_format_string()`]: crate::format_args::get_args_and_format_string() |
10 | #[derive (Debug, Clone)] |
11 | pub struct SpanError { |
12 | pub err: Error, |
13 | pub span: Option<Span>, |
14 | } |
15 | |
16 | impl SpanError { |
17 | /// Creates a new `SpanError`. |
18 | pub fn new(err: Error, span: Option<Span>) -> Self { |
19 | Self { err, span } |
20 | } |
21 | } |
22 | |
23 | /// Manual implementation because [`Span`] is not [`PartialEq`] and can be ignored when comparing. |
24 | impl PartialEq for SpanError { |
25 | fn eq(&self, other: &SpanError) -> bool { |
26 | self.err == other.err |
27 | } |
28 | } |
29 | |
30 | impl From<Error> for SpanError { |
31 | fn from(err: Error) -> SpanError { |
32 | SpanError { err, span: None } |
33 | } |
34 | } |
35 | |
36 | impl ToTokens for SpanError { |
37 | fn to_tokens(&self, tokens: &mut TokenStream2) { |
38 | let span: Span = self.span.unwrap_or_else(Span::call_site); |
39 | let token_stream_err: TokenStream = syn::Error::new(span, self.err.clone()).to_compile_error(); |
40 | token_stream_err.to_tokens(tokens); |
41 | } |
42 | } |
43 | |
44 | /// All possible errors which can occur when calling one of the three public macros. |
45 | #[derive (Debug, PartialEq, Clone)] |
46 | pub enum Error { |
47 | /// Error during the initial parsing of the macro arguments. |
48 | Parse(String), |
49 | /// The first macro argument is not a string literal. |
50 | MustBeStringLiteral, |
51 | /// Unable to parse a tag. |
52 | UnableToParseTag(String), |
53 | /// An error occured while parsing a color tag. |
54 | ParseTag(String), |
55 | /// A "{" character has not been closed in the format string. |
56 | UnclosedPlaceholder, |
57 | /// A "<" character has not been closed in the format string. |
58 | UnclosedTag, |
59 | /// Trying to close a previous tag, while there are no open tag. |
60 | NoTagToClose, |
61 | /// Trying to close a previous tag which does not match, like "<red>...</blue". |
62 | MismatchCloseTag(String, String), |
63 | /// Only one argument is allowed for the [`cstr!()`] and ['`untagged!()`] macros. |
64 | #[cfg (not(feature = "terminfo" ))] |
65 | TooManyArgs, |
66 | /// Only one argument is allowed for the ['`untagged!()`] macro. |
67 | #[cfg (feature = "terminfo" )] |
68 | TooManyArgs, |
69 | } |
70 | |
71 | impl fmt::Display for Error { |
72 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
73 | let msg: String = match self { |
74 | Self::Parse(msg: &String) => msg.clone(), |
75 | Self::MustBeStringLiteral => "Format string must be a string literal" .to_owned(), |
76 | Self::UnableToParseTag(tag: &String) => format!("Unable to parse the tag {}" , tag), |
77 | Self::ParseTag(detail: &String) => detail.clone(), |
78 | Self::UnclosedPlaceholder => "Unclosed placeholder" .to_owned(), |
79 | Self::UnclosedTag => "Unclosed color tag" .to_owned(), |
80 | Self::NoTagToClose => "No color tag to close" .to_owned(), |
81 | Self::MismatchCloseTag(tag1: &String, tag2: &String) => { |
82 | format!("Mismatch close tag between {} and {}" , tag1, tag2) |
83 | } |
84 | Self::TooManyArgs => "Too many arguments" .to_owned(), |
85 | }; |
86 | write!(f, " {}" , msg) |
87 | } |
88 | } |
89 | |