1use proc_macro2::Span;
8use quote::quote;
9
10pub type Result<T> = std::result::Result<T, Error>;
11
12#[derive(Debug)]
20pub struct Error {
21 span: Span,
22 kind: ErrorKind,
23}
24
25#[derive(Debug)]
27pub enum ErrorKind {
28 MissingWrapper {
30 expected: &'static str,
31 found: String,
32 suggestion: String,
33 },
34 EmptyGenericArgs {
36 wrapper: &'static str,
37 },
38 UnsupportedType {
40 name: String,
41 reason: &'static str,
42 suggestion: &'static str,
43 },
44 InvalidAttrValue {
46 attr: String,
47 expected: &'static str,
48 found: String,
49 },
50 MissingRequiredAttr {
52 attr: &'static str,
53 context: &'static str,
54 },
55 ConflictingAttrs {
57 first: String,
58 second: String,
59 suggestion: String,
60 },
61 MissingReturnType {
63 fn_name: String,
64 },
65 InvalidHandlerSig {
67 fn_name: String,
68 reason: &'static str,
69 },
70 UnknownKey {
72 key: String,
73 valid_keys: &'static [&'static str],
74 },
75 SynError(syn::Error),
77}
78
79impl Error {
84 pub fn missing_wrapper(span: Span, expected: &'static str, found: &syn::Type) -> Self {
85 let found_str = type_display(found);
86 Self {
87 span,
88 kind: ErrorKind::MissingWrapper {
89 suggestion: format!("wrap the type: `{}<{}>`", expected, found_str),
90 expected,
91 found: found_str,
92 },
93 }
94 }
95
96 pub fn empty_generic_args(span: Span, wrapper: &'static str) -> Self {
97 Self {
98 span,
99 kind: ErrorKind::EmptyGenericArgs { wrapper },
100 }
101 }
102
103 pub fn unsupported_type(
104 span: Span,
105 ty: &syn::Type,
106 reason: &'static str,
107 suggestion: &'static str,
108 ) -> Self {
109 Self {
110 span,
111 kind: ErrorKind::UnsupportedType {
112 name: type_display(ty),
113 reason,
114 suggestion,
115 },
116 }
117 }
118
119 pub fn invalid_attr_value(
120 span: Span,
121 attr: &str,
122 expected: &'static str,
123 found: &str,
124 ) -> Self {
125 Self {
126 span,
127 kind: ErrorKind::InvalidAttrValue {
128 attr: attr.to_string(),
129 expected,
130 found: found.to_string(),
131 },
132 }
133 }
134
135 pub fn missing_required_attr(
136 span: Span,
137 attr: &'static str,
138 context: &'static str,
139 ) -> Self {
140 Self {
141 span,
142 kind: ErrorKind::MissingRequiredAttr { attr, context },
143 }
144 }
145
146 pub fn conflicting_attrs(span: Span, first: &str, second: &str) -> Self {
147 Self {
148 span,
149 kind: ErrorKind::ConflictingAttrs {
150 suggestion: format!("remove either `{}` or `{}`", first, second),
151 first: first.to_string(),
152 second: second.to_string(),
153 },
154 }
155 }
156
157 pub fn missing_return_type(span: Span, fn_name: &str) -> Self {
158 Self {
159 span,
160 kind: ErrorKind::MissingReturnType {
161 fn_name: fn_name.to_string(),
162 },
163 }
164 }
165
166 pub fn invalid_handler_sig(span: Span, fn_name: &str, reason: &'static str) -> Self {
167 Self {
168 span,
169 kind: ErrorKind::InvalidHandlerSig {
170 fn_name: fn_name.to_string(),
171 reason,
172 },
173 }
174 }
175
176 pub fn unknown_key(span: Span, key: &str, valid_keys: &'static [&'static str]) -> Self {
177 Self {
178 span,
179 kind: ErrorKind::UnknownKey {
180 key: key.to_string(),
181 valid_keys,
182 },
183 }
184 }
185
186 pub fn to_compile_error(&self) -> proc_macro2::TokenStream {
188 syn::Error::new(self.span, self.to_string()).to_compile_error()
189 }
190}
191
192impl std::fmt::Display for Error {
197 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198 use ErrorKind::*;
199 match &self.kind {
200 MissingWrapper { expected, found, suggestion } => write!(
201 f,
202 "expected `{}` wrapper, found `{}`\n = help: {}",
203 expected, found, suggestion
204 ),
205 EmptyGenericArgs { wrapper } => write!(
206 f,
207 "`{}` requires at least one type argument",
208 wrapper
209 ),
210 UnsupportedType { name, reason, suggestion } => write!(
211 f,
212 "unsupported type `{}`\n = note: {}\n = help: {}",
213 name, reason, suggestion
214 ),
215 InvalidAttrValue { attr, expected, found } => write!(
216 f,
217 "invalid value for `{}`\n = expected: {}\n = found: {}",
218 attr, expected, found
219 ),
220 MissingRequiredAttr { attr, context } => write!(
221 f,
222 "missing required attribute `{}`\n = help: {}",
223 attr, context
224 ),
225 ConflictingAttrs { first, second, suggestion } => write!(
226 f,
227 "conflicting attributes `{}` and `{}`\n = help: {}",
228 first, second, suggestion
229 ),
230 MissingReturnType { fn_name } => write!(
231 f,
232 "`{}` has no return type\n = help: add `-> Json<T>` or `-> Result<Json<T>, E>`",
233 fn_name
234 ),
235 InvalidHandlerSig { fn_name, reason } => write!(
236 f,
237 "invalid handler signature for `{}`\n = note: {}\n = help: return `Json<T>` or `Result<Json<T>, E>`",
238 fn_name, reason
239 ),
240 UnknownKey { key, valid_keys } => write!(
241 f,
242 "unknown key `{}`\n = valid keys: {}",
243 key,
244 valid_keys.join(", ")
245 ),
246 SynError(e) => write!(f, "{}", e),
247 }
248 }
249}
250
251impl std::error::Error for Error {}
252
253impl From<syn::Error> for Error {
254 fn from(e: syn::Error) -> Self {
255 Self {
256 span: e.span(),
257 kind: ErrorKind::SynError(e),
258 }
259 }
260}
261
262pub(crate) fn type_display(ty: &syn::Type) -> String {
269 quote!(#ty).to_string().replace(' ', "")
270}
271
272#[cfg(test)]
277mod tests {
278 use super::*;
279 use proc_macro2::Span;
280
281 fn span() -> Span {
282 Span::call_site()
283 }
284
285 #[test]
286 fn missing_wrapper_message() {
287 let ty: syn::Type = syn::parse_str("Vec<Planet>").unwrap();
288 let err = Error::missing_wrapper(span(), "Json", &ty);
289 let msg = err.to_string();
290 assert!(msg.contains("expected `Json` wrapper"));
291 assert!(msg.contains("Vec<Planet>"));
292 assert!(msg.contains("help:"));
293 assert!(msg.contains("Json<Vec<Planet>>"));
294 }
295
296 #[test]
297 fn empty_generic_args_message() {
298 let err = Error::empty_generic_args(span(), "Result");
299 let msg = err.to_string();
300 assert!(msg.contains("`Result` requires at least one type argument"));
301 }
302
303 #[test]
304 fn missing_required_attr_message() {
305 let err = Error::missing_required_attr(span(), "method", "add `method = \"GET\"` to #[orpc]");
306 let msg = err.to_string();
307 assert!(msg.contains("missing required attribute `method`"));
308 assert!(msg.contains("help:"));
309 }
310
311 #[test]
312 fn unknown_key_message() {
313 let err = Error::unknown_key(span(), "routes", &["method", "path", "stream_event"]);
314 let msg = err.to_string();
315 assert!(msg.contains("unknown key `routes`"));
316 assert!(msg.contains("method"));
317 assert!(msg.contains("path"));
318 assert!(msg.contains("stream_event"));
319 }
320
321 #[test]
322 fn conflicting_attrs_message() {
323 let err = Error::conflicting_attrs(span(), "method", "methods");
324 let msg = err.to_string();
325 assert!(msg.contains("conflicting attributes"));
326 assert!(msg.contains("help: remove either"));
327 }
328
329 #[test]
330 fn missing_return_type_message() {
331 let err = Error::missing_return_type(span(), "handle_ping");
332 let msg = err.to_string();
333 assert!(msg.contains("`handle_ping` has no return type"));
334 assert!(msg.contains("Json<T>"));
335 }
336
337 #[test]
338 fn invalid_handler_sig_message() {
339 let err = Error::invalid_handler_sig(span(), "my_handler", "return type is not Json<T>");
340 let msg = err.to_string();
341 assert!(msg.contains("invalid handler signature for `my_handler`"));
342 assert!(msg.contains("return type is not Json<T>"));
343 }
344
345 #[test]
346 fn syn_error_forwarded() {
347 let syn_err = syn::Error::new(span(), "raw syn error");
348 let err = Error::from(syn_err);
349 assert!(err.to_string().contains("raw syn error"));
350 }
351
352 #[test]
353 fn to_compile_error_is_nonempty() {
354 let err = Error::empty_generic_args(span(), "Json");
355 let tokens = err.to_compile_error();
356 assert!(!tokens.is_empty());
357 }
358}