1 | use std::borrow::Cow; |
2 | use std::fmt; |
3 | |
4 | use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; |
5 | |
6 | trait WithSpan { |
7 | fn with_span(self, span: Span) -> Self; |
8 | } |
9 | |
10 | impl WithSpan for TokenTree { |
11 | fn with_span(mut self, span: Span) -> Self { |
12 | self.set_span(span); |
13 | self |
14 | } |
15 | } |
16 | |
17 | pub(crate) enum Error { |
18 | MissingComponent { |
19 | name: &'static str, |
20 | span_start: Option<Span>, |
21 | span_end: Option<Span>, |
22 | }, |
23 | InvalidComponent { |
24 | name: &'static str, |
25 | value: String, |
26 | span_start: Option<Span>, |
27 | span_end: Option<Span>, |
28 | }, |
29 | #[cfg (any(feature = "formatting" , feature = "parsing" ))] |
30 | ExpectedString { |
31 | span_start: Option<Span>, |
32 | span_end: Option<Span>, |
33 | }, |
34 | UnexpectedToken { |
35 | tree: TokenTree, |
36 | }, |
37 | UnexpectedEndOfInput, |
38 | Custom { |
39 | message: Cow<'static, str>, |
40 | span_start: Option<Span>, |
41 | span_end: Option<Span>, |
42 | }, |
43 | } |
44 | |
45 | impl fmt::Display for Error { |
46 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
47 | match self { |
48 | Self::MissingComponent { name: &&'static str, .. } => write!(f, "missing component: {name}" ), |
49 | Self::InvalidComponent { name: &&'static str, value: &String, .. } => { |
50 | write!(f, "invalid component: {name} was {value}" ) |
51 | } |
52 | #[cfg (any(feature = "formatting" , feature = "parsing" ))] |
53 | Self::ExpectedString { .. } => f.write_str(data:"expected string literal" ), |
54 | Self::UnexpectedToken { tree: &TokenTree } => write!(f, "unexpected token: {tree}" ), |
55 | Self::UnexpectedEndOfInput => f.write_str(data:"unexpected end of input" ), |
56 | Self::Custom { message: &Cow<'static, str>, .. } => f.write_str(data:message), |
57 | } |
58 | } |
59 | } |
60 | |
61 | impl Error { |
62 | fn span_start(&self) -> Span { |
63 | match self { |
64 | Self::MissingComponent { span_start, .. } |
65 | | Self::InvalidComponent { span_start, .. } |
66 | | Self::Custom { span_start, .. } => *span_start, |
67 | #[cfg (any(feature = "formatting" , feature = "parsing" ))] |
68 | Self::ExpectedString { span_start, .. } => *span_start, |
69 | Self::UnexpectedToken { tree } => Some(tree.span()), |
70 | Self::UnexpectedEndOfInput => Some(Span::mixed_site()), |
71 | } |
72 | .unwrap_or_else(Span::mixed_site) |
73 | } |
74 | |
75 | fn span_end(&self) -> Span { |
76 | match self { |
77 | Self::MissingComponent { span_end, .. } |
78 | | Self::InvalidComponent { span_end, .. } |
79 | | Self::Custom { span_end, .. } => *span_end, |
80 | #[cfg (any(feature = "formatting" , feature = "parsing" ))] |
81 | Self::ExpectedString { span_end, .. } => *span_end, |
82 | Self::UnexpectedToken { tree, .. } => Some(tree.span()), |
83 | Self::UnexpectedEndOfInput => Some(Span::mixed_site()), |
84 | } |
85 | .unwrap_or_else(|| self.span_start()) |
86 | } |
87 | |
88 | pub(crate) fn to_compile_error(&self) -> TokenStream { |
89 | let (start, end) = (self.span_start(), self.span_end()); |
90 | |
91 | [ |
92 | TokenTree::from(Punct::new(':' , Spacing::Joint)).with_span(start), |
93 | TokenTree::from(Punct::new(':' , Spacing::Alone)).with_span(start), |
94 | TokenTree::from(Ident::new("core" , start)), |
95 | TokenTree::from(Punct::new(':' , Spacing::Joint)).with_span(start), |
96 | TokenTree::from(Punct::new(':' , Spacing::Alone)).with_span(start), |
97 | TokenTree::from(Ident::new("compile_error" , start)), |
98 | TokenTree::from(Punct::new('!' , Spacing::Alone)).with_span(start), |
99 | TokenTree::from(Group::new( |
100 | Delimiter::Parenthesis, |
101 | TokenStream::from( |
102 | TokenTree::from(Literal::string(&self.to_string())).with_span(end), |
103 | ), |
104 | )) |
105 | .with_span(end), |
106 | ] |
107 | .iter() |
108 | .cloned() |
109 | .collect() |
110 | } |
111 | |
112 | /// Like `to_compile_error`, but for use in macros that produce items. |
113 | #[cfg (all(feature = "serde" , any(feature = "formatting" , feature = "parsing" )))] |
114 | pub(crate) fn to_compile_error_standalone(&self) -> TokenStream { |
115 | let end = self.span_end(); |
116 | self.to_compile_error() |
117 | .into_iter() |
118 | .chain(std::iter::once( |
119 | TokenTree::from(Punct::new(';' , Spacing::Alone)).with_span(end), |
120 | )) |
121 | .collect() |
122 | } |
123 | } |
124 | |