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: 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)]
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: 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