kefta_core/
error.rs

1//! library error types
2
3use std::fmt::{Debug, Formatter};
4use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
5
6/// alias for `Result<T, KeftaError>`
7pub type KeftaResult<T> = Result<T, KeftaError>;
8
9/// an error for parsing tokens into nodes
10#[derive(Debug)]
11pub enum KeftaTokenError {
12    /// expected a token, found end of stream
13    ExpectedToken { span: Span },
14    /// expected a type, found otherwise
15    Expected {
16        /// the expected type
17        expected: &'static str,
18        /// further description
19        description: Option<&'static str>,
20        /// the tokens found
21        found: TokenTree
22    },
23    /// a generic message
24    Message(&'static str, Span)
25}
26
27/// an error while parsing attributes
28pub enum KeftaError {
29    /// an error while parsing tokens
30    TokenError(KeftaTokenError),
31
32    /// expected a marker, found otherwise
33    ExpectedMarker { ident: Ident },
34    /// expected a value, found otherwise
35    ExpectedValue { ident: Ident },
36    /// expected a container, found otherwise
37    ExpectedContainer { ident: Ident },
38
39    /// expected a type at the given span, found otherwise
40    Expected {
41        /// the expected type
42        expected: KeftaExpected,
43        /// the span
44        span: Span,
45    },
46
47    /// found multiple nodes, but only expected one.
48    Multiple {
49        key: String,
50        count: usize,
51        span: Span,
52    },
53
54    /// the node is required, but was not found.
55    Required {
56        key: String,
57        multiple: bool,
58    },
59
60    /// a generic message
61    Message {
62        message: String,
63        span: Option<Span>,
64    },
65
66    /// an error for `syn` compatibility
67    #[cfg(feature = "syn")]
68    Syn(syn::Error)
69}
70
71/// an expected type in an error
72#[derive(Debug)]
73pub enum KeftaExpected {
74    Literal,
75    Punct,
76    Ident,
77    Group,
78
79    Delimiter(Delimiter),
80
81    CharLiteral,
82    StringLiteral,
83    ByteLiteral,
84    NumericLiteral,
85    BooleanLiteral,
86}
87
88
89impl Debug for KeftaError {
90    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
91        match self {
92            KeftaError::TokenError(error) => Debug::fmt(&error, f),
93            #[cfg(feature = "syn")]
94            KeftaError::Syn(x) => Debug::fmt(&x, f),
95
96            KeftaError::ExpectedMarker { ident } =>
97                f.debug_tuple("ExpectedMarker")
98                    .field(ident)
99                    .finish(),
100            KeftaError::ExpectedValue { ident } =>
101                f.debug_tuple("ExpectedValue")
102                    .field(ident)
103                    .finish(),
104            KeftaError::ExpectedContainer { ident } =>
105                f.debug_tuple("ExpectedContainer")
106                    .field(ident)
107                    .finish(),
108            KeftaError::Expected { expected, span } =>
109                f.debug_tuple("ExpectedValue")
110                    .field(expected)
111                    .field(span)
112                    .finish(),
113            KeftaError::Multiple { key, .. } =>
114                f.debug_tuple("ExpectedMultiple")
115                    .field(key)
116                    .finish(),
117            KeftaError::Required { key, .. } =>
118                f.debug_tuple("RequiredAttr")
119                    .field(key)
120                    .finish(),
121            KeftaError::Message { message, .. } => Debug::fmt(&message, f)
122        }
123    }
124}
125
126impl KeftaError {
127    pub fn build(self) -> (Span, String) {
128        match self {
129            #[cfg(feature = "syn")]
130            KeftaError::Syn(e) => (e.span(), e.to_string()),
131
132            KeftaError::TokenError(error) => match error {
133                KeftaTokenError::ExpectedToken { span } =>
134                    (span, "expected a token, found the end of the stream".to_string()),
135                KeftaTokenError::Expected { expected, description, found } =>
136                    (found.span(), format!(
137                        "expected {}{} but found {}",
138                        expected,
139                        match description {
140                            None => ",".to_string(),
141                            Some(desc) => format!(" [{}]", desc.to_string()),
142                        },
143                        match found {
144                            TokenTree::Ident(ident) => format!("ident `{}`", ident.to_string()),
145                            TokenTree::Punct(punct) => format!("a `{}` token", punct.as_char()),
146                            TokenTree::Literal(literal) => format!("literal `{}`", literal),
147                            TokenTree::Group(group) => format!("a `{}` group", delimiter_str(group.delimiter())),
148                        },
149                    )),
150                KeftaTokenError::Message(msg, span) => (span, msg.to_string())
151            },
152
153            KeftaError::ExpectedMarker { ident } =>
154                (ident.span(), "expected a `marker` type attribute".to_string()),
155            KeftaError::ExpectedValue { ident } =>
156                (ident.span(), "expected a value".to_string()),
157            KeftaError::ExpectedContainer { ident } =>
158                (ident.span(), "expected a `container` attribute".to_string()),
159
160            KeftaError::Expected { expected, span } =>
161                (span, format!(
162                    "expected `{}`",
163                    match expected {
164                        KeftaExpected::Literal => "a literal",
165                        KeftaExpected::Punct => "a punct token",
166                        KeftaExpected::Ident => "an identifier",
167                        KeftaExpected::Group => "a group",
168                        KeftaExpected::Delimiter(_delimiter) => "a group", // todo
169                        KeftaExpected::CharLiteral => "a character literal (char)",
170                        KeftaExpected::StringLiteral => "a string literal",
171                        KeftaExpected::ByteLiteral => "a byte-string literal (b\"\")",
172                        KeftaExpected::NumericLiteral => "a numeric literal",
173                        KeftaExpected::BooleanLiteral => "a boolean literal (true/false)"
174                    }
175                )),
176
177            KeftaError::Multiple { key, count, span } =>
178                (span, format!(
179                    "found {} occurrences for `{}`, but only expected one",
180                    count,
181                    key
182                )),
183
184            KeftaError::Required { key, .. } =>
185                (Span::call_site(), format!(
186                    "the attribute `{}` is required", key
187                )),
188
189            KeftaError::Message { message, span } =>
190                (span.unwrap_or_else(|| Span::call_site()), message),
191
192            //this @ _ => syn::Error::new(Span::call_site(), format!("{:?}", this))
193        }
194    }
195
196    pub fn to_compile_error(self) -> TokenStream {
197        // from https://docs.rs/syn/latest/src/syn/error.rs.html#248
198        let (span, msg) = self.build();
199
200        // compile_error!($message)
201        TokenStream::from_iter(vec![
202            TokenTree::Ident(Ident::new("compile_error", span.clone())),
203            TokenTree::Punct({
204                let mut punct = Punct::new('!', Spacing::Alone);
205                punct.set_span(span.clone());
206                punct
207            }),
208            TokenTree::Group({
209                let mut group = Group::new(Delimiter::Brace, {
210                    TokenStream::from_iter(vec![TokenTree::Literal({
211                        let mut string = Literal::string(&msg);
212                        string.set_span(span.clone());
213                        string
214                    })])
215                });
216                group.set_span(span.clone());
217                group
218            }),
219        ])
220    }
221
222    #[cfg(feature="syn")]
223    pub fn into_syn(self) -> syn::Error {
224        match self {
225            KeftaError::Syn(e) => e,
226            e @ _ => {
227                let (span, msg) = e.build();
228                syn::Error::new(span, msg)
229            }
230        }
231    }
232}
233
234#[cfg(feature="syn")]
235impl Into<syn::Error> for KeftaError {
236    fn into(self) -> syn::Error {
237        self.into_syn()
238    }
239}
240
241
242fn delimiter_str(delimiter: Delimiter) -> &'static str {
243    match delimiter {
244        Delimiter::Parenthesis => "()",
245        Delimiter::Brace => "{}",
246        Delimiter::Bracket => "[]",
247        Delimiter::None => "_",
248    }
249}