| 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 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 | |