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