color_print_proc_macro/
error.rs

1use std::fmt;
2
3use proc_macro2::{Span, TokenStream as TokenStream2};
4use 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)]
11pub struct SpanError {
12    pub err: Error,
13    pub span: Option<Span>,
14}
15
16impl 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.
24impl PartialEq for SpanError {
25    fn eq(&self, other: &SpanError) -> bool {
26        self.err == other.err
27    }
28}
29
30impl From<Error> for SpanError {
31    fn from(err: Error) -> SpanError {
32        SpanError { err, span: None }
33    }
34}
35
36impl ToTokens for SpanError {
37    fn to_tokens(&self, tokens: &mut TokenStream2) {
38        let span = self.span.unwrap_or_else(Span::call_site);
39        let token_stream_err = 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)]
46pub 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
71impl fmt::Display for Error {
72    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73        let msg = match self {
74            Self::Parse(msg) => msg.clone(),
75            Self::MustBeStringLiteral => "Format string must be a string literal".to_owned(),
76            Self::UnableToParseTag(tag) => format!("Unable to parse the tag {}", tag),
77            Self::ParseTag(detail) => 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, tag2) => {
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}