axum_macros/lib.rs
1//! Macros for [`axum`].
2//!
3//! [`axum`]: https://crates.io/crates/axum
4
5#![cfg_attr(docsrs, feature(doc_cfg))]
6#![cfg_attr(test, allow(clippy::float_cmp))]
7#![cfg_attr(not(test), warn(clippy::print_stdout, clippy::dbg_macro))]
8
9use debug_handler::FunctionKind;
10use proc_macro::TokenStream;
11use quote::{quote, ToTokens};
12use syn::{parse::Parse, Type};
13
14mod attr_parsing;
15#[cfg(feature = "__private")]
16mod axum_test;
17mod debug_handler;
18mod from_ref;
19mod from_request;
20mod typed_path;
21mod with_position;
22
23use from_request::Trait::{FromRequest, FromRequestParts};
24
25/// Derive an implementation of [`FromRequest`].
26///
27/// Supports generating two kinds of implementations:
28/// 1. One that extracts each field individually.
29/// 2. Another that extracts the whole type at once via another extractor.
30///
31/// # Each field individually
32///
33/// By default `#[derive(FromRequest)]` will call `FromRequest::from_request` for each field:
34///
35/// ```
36/// use axum_macros::FromRequest;
37/// use axum::{
38/// extract::Extension,
39/// body::Bytes,
40/// };
41/// use axum_extra::{
42/// TypedHeader,
43/// headers::ContentType,
44/// };
45///
46/// #[derive(FromRequest)]
47/// struct MyExtractor {
48/// state: Extension<State>,
49/// content_type: TypedHeader<ContentType>,
50/// request_body: Bytes,
51/// }
52///
53/// #[derive(Clone)]
54/// struct State {
55/// // ...
56/// }
57///
58/// async fn handler(extractor: MyExtractor) {}
59/// ```
60///
61/// This requires that each field is an extractor (i.e. implements [`FromRequest`]).
62///
63/// Note that only the last field can consume the request body. Therefore this doesn't compile:
64///
65/// ```compile_fail
66/// use axum_macros::FromRequest;
67/// use axum::body::Bytes;
68///
69/// #[derive(FromRequest)]
70/// struct MyExtractor {
71/// // only the last field can implement `FromRequest`
72/// // other fields must only implement `FromRequestParts`
73/// bytes: Bytes,
74/// string: String,
75/// }
76/// ```
77///
78/// ## Extracting via another extractor
79///
80/// You can use `#[from_request(via(...))]` to extract a field via another extractor, meaning the
81/// field itself doesn't need to implement `FromRequest`:
82///
83/// ```
84/// use axum_macros::FromRequest;
85/// use axum::{
86/// extract::Extension,
87/// body::Bytes,
88/// };
89/// use axum_extra::{
90/// TypedHeader,
91/// headers::ContentType,
92/// };
93///
94/// #[derive(FromRequest)]
95/// struct MyExtractor {
96/// // This will extracted via `Extension::<State>::from_request`
97/// #[from_request(via(Extension))]
98/// state: State,
99/// // and this via `TypedHeader::<ContentType>::from_request`
100/// #[from_request(via(TypedHeader))]
101/// content_type: ContentType,
102/// // Can still be combined with other extractors
103/// request_body: Bytes,
104/// }
105///
106/// #[derive(Clone)]
107/// struct State {
108/// // ...
109/// }
110///
111/// async fn handler(extractor: MyExtractor) {}
112/// ```
113///
114/// Note this requires the via extractor to be a generic newtype struct (a tuple struct with
115/// exactly one public field) that implements `FromRequest`:
116///
117/// ```
118/// pub struct ViaExtractor<T>(pub T);
119///
120/// // impl<T, S> FromRequest<S> for ViaExtractor<T> { ... }
121/// ```
122///
123/// More complex via extractors are not supported and require writing a manual implementation.
124///
125/// ## Optional fields
126///
127/// `#[from_request(via(...))]` supports `Option<_>` and `Result<_, _>` to make fields optional:
128///
129/// ```
130/// use axum_macros::FromRequest;
131/// use axum_extra::{
132/// TypedHeader,
133/// headers::{ContentType, UserAgent},
134/// typed_header::TypedHeaderRejection,
135/// };
136///
137/// #[derive(FromRequest)]
138/// struct MyExtractor {
139/// // This will extracted via `Option::<TypedHeader<ContentType>>::from_request`
140/// #[from_request(via(TypedHeader))]
141/// content_type: Option<ContentType>,
142/// // This will extracted via
143/// // `Result::<TypedHeader<UserAgent>, TypedHeaderRejection>::from_request`
144/// #[from_request(via(TypedHeader))]
145/// user_agent: Result<UserAgent, TypedHeaderRejection>,
146/// }
147///
148/// async fn handler(extractor: MyExtractor) {}
149/// ```
150///
151/// ## The rejection
152///
153/// By default [`axum::response::Response`] will be used as the rejection. You can also use your own
154/// rejection type with `#[from_request(rejection(YourType))]`:
155///
156/// ```
157/// use axum::{
158/// extract::{
159/// rejection::{ExtensionRejection, StringRejection},
160/// FromRequest,
161/// },
162/// Extension,
163/// response::{Response, IntoResponse},
164/// };
165///
166/// #[derive(FromRequest)]
167/// #[from_request(rejection(MyRejection))]
168/// struct MyExtractor {
169/// state: Extension<String>,
170/// body: String,
171/// }
172///
173/// struct MyRejection(Response);
174///
175/// // This tells axum how to convert `Extension`'s rejections into `MyRejection`
176/// impl From<ExtensionRejection> for MyRejection {
177/// fn from(rejection: ExtensionRejection) -> Self {
178/// // ...
179/// # todo!()
180/// }
181/// }
182///
183/// // This tells axum how to convert `String`'s rejections into `MyRejection`
184/// impl From<StringRejection> for MyRejection {
185/// fn from(rejection: StringRejection) -> Self {
186/// // ...
187/// # todo!()
188/// }
189/// }
190///
191/// // All rejections must implement `IntoResponse`
192/// impl IntoResponse for MyRejection {
193/// fn into_response(self) -> Response {
194/// self.0
195/// }
196/// }
197/// ```
198///
199/// ## Concrete state
200///
201/// If the extraction can be done only for a concrete state, that type can be specified with
202/// `#[from_request(state(YourState))]`:
203///
204/// ```
205/// use axum::extract::{FromRequest, FromRequestParts};
206///
207/// #[derive(Clone)]
208/// struct CustomState;
209///
210/// struct MyInnerType;
211///
212/// impl FromRequestParts<CustomState> for MyInnerType {
213/// // ...
214/// # type Rejection = ();
215///
216/// # async fn from_request_parts(
217/// # _parts: &mut axum::http::request::Parts,
218/// # _state: &CustomState
219/// # ) -> Result<Self, Self::Rejection> {
220/// # todo!()
221/// # }
222/// }
223///
224/// #[derive(FromRequest)]
225/// #[from_request(state(CustomState))]
226/// struct MyExtractor {
227/// custom: MyInnerType,
228/// body: String,
229/// }
230/// ```
231///
232/// This is not needed for a `State<T>` as the type is inferred in that case.
233///
234/// ```
235/// use axum::extract::{FromRequest, FromRequestParts, State};
236///
237/// #[derive(Clone)]
238/// struct CustomState;
239///
240/// #[derive(FromRequest)]
241/// struct MyExtractor {
242/// custom: State<CustomState>,
243/// body: String,
244/// }
245/// ```
246///
247/// # The whole type at once
248///
249/// By using `#[from_request(via(...))]` on the container you can extract the whole type at once,
250/// instead of each field individually:
251///
252/// ```
253/// use axum_macros::FromRequest;
254/// use axum::extract::Extension;
255///
256/// // This will extracted via `Extension::<State>::from_request`
257/// #[derive(Clone, FromRequest)]
258/// #[from_request(via(Extension))]
259/// struct State {
260/// // ...
261/// }
262///
263/// async fn handler(state: State) {}
264/// ```
265///
266/// The rejection will be the "via extractors"'s rejection. For the previous example that would be
267/// [`axum::extract::rejection::ExtensionRejection`].
268///
269/// You can use a different rejection type with `#[from_request(rejection(YourType))]`:
270///
271/// ```
272/// use axum_macros::FromRequest;
273/// use axum::{
274/// extract::{Extension, rejection::ExtensionRejection},
275/// response::{IntoResponse, Response},
276/// Json,
277/// http::StatusCode,
278/// };
279/// use serde_json::json;
280///
281/// // This will extracted via `Extension::<State>::from_request`
282/// #[derive(Clone, FromRequest)]
283/// #[from_request(
284/// via(Extension),
285/// // Use your own rejection type
286/// rejection(MyRejection),
287/// )]
288/// struct State {
289/// // ...
290/// }
291///
292/// struct MyRejection(Response);
293///
294/// // This tells axum how to convert `Extension`'s rejections into `MyRejection`
295/// impl From<ExtensionRejection> for MyRejection {
296/// fn from(rejection: ExtensionRejection) -> Self {
297/// let response = (
298/// StatusCode::INTERNAL_SERVER_ERROR,
299/// Json(json!({ "error": "Something went wrong..." })),
300/// ).into_response();
301///
302/// MyRejection(response)
303/// }
304/// }
305///
306/// // All rejections must implement `IntoResponse`
307/// impl IntoResponse for MyRejection {
308/// fn into_response(self) -> Response {
309/// self.0
310/// }
311/// }
312///
313/// async fn handler(state: State) {}
314/// ```
315///
316/// This allows you to wrap other extractors and easily customize the rejection:
317///
318/// ```
319/// use axum_macros::FromRequest;
320/// use axum::{
321/// extract::{Extension, rejection::JsonRejection},
322/// response::{IntoResponse, Response},
323/// http::StatusCode,
324/// };
325/// use serde_json::json;
326/// use serde::Deserialize;
327///
328/// // create an extractor that internally uses `axum::Json` but has a custom rejection
329/// #[derive(FromRequest)]
330/// #[from_request(via(axum::Json), rejection(MyRejection))]
331/// struct MyJson<T>(T);
332///
333/// struct MyRejection(Response);
334///
335/// impl From<JsonRejection> for MyRejection {
336/// fn from(rejection: JsonRejection) -> Self {
337/// let response = (
338/// StatusCode::INTERNAL_SERVER_ERROR,
339/// axum::Json(json!({ "error": rejection.to_string() })),
340/// ).into_response();
341///
342/// MyRejection(response)
343/// }
344/// }
345///
346/// impl IntoResponse for MyRejection {
347/// fn into_response(self) -> Response {
348/// self.0
349/// }
350/// }
351///
352/// #[derive(Deserialize)]
353/// struct Payload {}
354///
355/// async fn handler(
356/// // make sure to use `MyJson` and not `axum::Json`
357/// MyJson(payload): MyJson<Payload>,
358/// ) {}
359/// ```
360///
361/// # Known limitations
362///
363/// Generics are only supported on tuple structs with exactly one field. Thus this doesn't work
364///
365/// ```compile_fail
366/// #[derive(axum_macros::FromRequest)]
367/// struct MyExtractor<T> {
368/// thing: Option<T>,
369/// }
370/// ```
371///
372/// [`FromRequest`]: https://docs.rs/axum/0.8/axum/extract/trait.FromRequest.html
373/// [`axum::response::Response`]: https://docs.rs/axum/0.8/axum/response/type.Response.html
374/// [`axum::extract::rejection::ExtensionRejection`]: https://docs.rs/axum/0.8/axum/extract/rejection/enum.ExtensionRejection.html
375#[proc_macro_derive(FromRequest, attributes(from_request))]
376pub fn derive_from_request(item: TokenStream) -> TokenStream {
377 expand_with(item, |item| from_request::expand(item, FromRequest))
378}
379
380/// Derive an implementation of [`FromRequestParts`].
381///
382/// This works similarly to `#[derive(FromRequest)]` except it uses [`FromRequestParts`]. All the
383/// same options are supported.
384///
385/// # Example
386///
387/// ```
388/// use axum_macros::FromRequestParts;
389/// use axum::{
390/// extract::Query,
391/// };
392/// use axum_extra::{
393/// TypedHeader,
394/// headers::ContentType,
395/// };
396/// use std::collections::HashMap;
397///
398/// #[derive(FromRequestParts)]
399/// struct MyExtractor {
400/// #[from_request(via(Query))]
401/// query_params: HashMap<String, String>,
402/// content_type: TypedHeader<ContentType>,
403/// }
404///
405/// async fn handler(extractor: MyExtractor) {}
406/// ```
407///
408/// # Cannot extract the body
409///
410/// [`FromRequestParts`] cannot extract the request body:
411///
412/// ```compile_fail
413/// use axum_macros::FromRequestParts;
414///
415/// #[derive(FromRequestParts)]
416/// struct MyExtractor {
417/// body: String,
418/// }
419/// ```
420///
421/// Use `#[derive(FromRequest)]` for that.
422///
423/// [`FromRequestParts`]: https://docs.rs/axum/0.8/axum/extract/trait.FromRequestParts.html
424#[proc_macro_derive(FromRequestParts, attributes(from_request))]
425pub fn derive_from_request_parts(item: TokenStream) -> TokenStream {
426 expand_with(item, |item| from_request::expand(item, FromRequestParts))
427}
428
429/// Generates better error messages when applied to handler functions.
430///
431/// While using [`axum`], you can get long error messages for simple mistakes. For example:
432///
433/// ```compile_fail
434/// use axum::{routing::get, Router};
435///
436/// #[tokio::main]
437/// async fn main() {
438/// let app = Router::new().route("/", get(handler));
439///
440/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
441/// axum::serve(listener, app).await.unwrap();
442/// }
443///
444/// fn handler() -> &'static str {
445/// "Hello, world"
446/// }
447/// ```
448///
449/// You will get a long error message about function not implementing [`Handler`] trait. But why
450/// does this function not implement it? To figure it out, the [`debug_handler`] macro can be used.
451///
452/// ```compile_fail
453/// # use axum::{routing::get, Router};
454/// # use axum_macros::debug_handler;
455/// #
456/// # #[tokio::main]
457/// # async fn main() {
458/// # let app = Router::new().route("/", get(handler));
459/// #
460/// # let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
461/// # axum::serve(listener, app).await.unwrap();
462/// # }
463/// #
464/// #[debug_handler]
465/// fn handler() -> &'static str {
466/// "Hello, world"
467/// }
468/// ```
469///
470/// ```text
471/// error: handlers must be async functions
472/// --> main.rs:xx:1
473/// |
474/// xx | fn handler() -> &'static str {
475/// | ^^
476/// ```
477///
478/// As the error message says, handler function needs to be async.
479///
480/// ```no_run
481/// use axum::{routing::get, Router, debug_handler};
482///
483/// #[tokio::main]
484/// async fn main() {
485/// let app = Router::new().route("/", get(handler));
486///
487/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
488/// axum::serve(listener, app).await.unwrap();
489/// }
490///
491/// #[debug_handler]
492/// async fn handler() -> &'static str {
493/// "Hello, world"
494/// }
495/// ```
496///
497/// # Changing state type
498///
499/// By default `#[debug_handler]` assumes your state type is `()` unless your handler has a
500/// [`axum::extract::State`] argument:
501///
502/// ```
503/// use axum::{debug_handler, extract::State};
504///
505/// #[debug_handler]
506/// async fn handler(
507/// // this makes `#[debug_handler]` use `AppState`
508/// State(state): State<AppState>,
509/// ) {}
510///
511/// #[derive(Clone)]
512/// struct AppState {}
513/// ```
514///
515/// If your handler takes multiple [`axum::extract::State`] arguments or you need to otherwise
516/// customize the state type you can set it with `#[debug_handler(state = ...)]`:
517///
518/// ```
519/// use axum::{debug_handler, extract::{State, FromRef}};
520///
521/// #[debug_handler(state = AppState)]
522/// async fn handler(
523/// State(app_state): State<AppState>,
524/// State(inner_state): State<InnerState>,
525/// ) {}
526///
527/// #[derive(Clone)]
528/// struct AppState {
529/// inner: InnerState,
530/// }
531///
532/// #[derive(Clone)]
533/// struct InnerState {}
534///
535/// impl FromRef<AppState> for InnerState {
536/// fn from_ref(state: &AppState) -> Self {
537/// state.inner.clone()
538/// }
539/// }
540/// ```
541///
542/// # Limitations
543///
544/// This macro does not work for functions in an `impl` block that don't have a `self` parameter:
545///
546/// ```compile_fail
547/// use axum::{debug_handler, extract::Path};
548///
549/// struct App {}
550///
551/// impl App {
552/// #[debug_handler]
553/// async fn handler(Path(_): Path<String>) {}
554/// }
555/// ```
556///
557/// This will yield an error similar to this:
558///
559/// ```text
560/// error[E0425]: cannot find function `__axum_macros_check_handler_0_from_request_check` in this scope
561// --> src/main.rs:xx:xx
562// |
563// xx | pub async fn handler(Path(_): Path<String>) {}
564// | ^^^^ not found in this scope
565/// ```
566///
567/// # Performance
568///
569/// This macro has no effect when compiled with the release profile. (eg. `cargo build --release`)
570///
571/// [`axum`]: https://docs.rs/axum/0.8
572/// [`Handler`]: https://docs.rs/axum/0.8/axum/handler/trait.Handler.html
573/// [`axum::extract::State`]: https://docs.rs/axum/0.8/axum/extract/struct.State.html
574/// [`debug_handler`]: macro@debug_handler
575#[proc_macro_attribute]
576pub fn debug_handler(_attr: TokenStream, input: TokenStream) -> TokenStream {
577 #[cfg(not(debug_assertions))]
578 return input;
579
580 #[cfg(debug_assertions)]
581 return expand_attr_with(_attr, input, |attrs, item_fn| {
582 debug_handler::expand(attrs, item_fn, FunctionKind::Handler)
583 });
584}
585
586/// Generates better error messages when applied to middleware functions.
587///
588/// This works similarly to [`#[debug_handler]`](macro@debug_handler) except for middleware using
589/// [`axum::middleware::from_fn`].
590///
591/// # Example
592///
593/// ```no_run
594/// use axum::{
595/// routing::get,
596/// extract::Request,
597/// response::Response,
598/// Router,
599/// middleware::{self, Next},
600/// debug_middleware,
601/// };
602///
603/// #[tokio::main]
604/// async fn main() {
605/// let app = Router::new()
606/// .route("/", get(|| async {}))
607/// .layer(middleware::from_fn(my_middleware));
608///
609/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
610/// axum::serve(listener, app).await.unwrap();
611/// }
612///
613/// // if this wasn't a valid middleware function #[debug_middleware] would
614/// // improve compile error
615/// #[debug_middleware]
616/// async fn my_middleware(
617/// request: Request,
618/// next: Next,
619/// ) -> Response {
620/// next.run(request).await
621/// }
622/// ```
623///
624/// # Performance
625///
626/// This macro has no effect when compiled with the release profile. (eg. `cargo build --release`)
627///
628/// [`axum`]: https://docs.rs/axum/latest
629/// [`axum::middleware::from_fn`]: https://docs.rs/axum/0.8/axum/middleware/fn.from_fn.html
630/// [`debug_middleware`]: macro@debug_middleware
631#[proc_macro_attribute]
632pub fn debug_middleware(_attr: TokenStream, input: TokenStream) -> TokenStream {
633 #[cfg(not(debug_assertions))]
634 return input;
635
636 #[cfg(debug_assertions)]
637 return expand_attr_with(_attr, input, |attrs, item_fn| {
638 debug_handler::expand(attrs, item_fn, FunctionKind::Middleware)
639 });
640}
641
642/// Private API: Do no use this!
643///
644/// Attribute macro to be placed on test functions that'll generate two functions:
645///
646/// 1. One identical to the function it was placed on.
647/// 2. One where calls to `Router::nest` has been replaced with `Router::nest_service`
648///
649/// This makes it easy to that `nest` and `nest_service` behaves in the same way, without having to
650/// manually write identical tests for both methods.
651#[cfg(feature = "__private")]
652#[proc_macro_attribute]
653#[doc(hidden)]
654pub fn __private_axum_test(_attr: TokenStream, input: TokenStream) -> TokenStream {
655 expand_attr_with(_attr, input, axum_test::expand)
656}
657
658/// Derive an implementation of [`axum_extra::routing::TypedPath`].
659///
660/// See that trait for more details.
661///
662/// [`axum_extra::routing::TypedPath`]: https://docs.rs/axum-extra/latest/axum_extra/routing/trait.TypedPath.html
663#[proc_macro_derive(TypedPath, attributes(typed_path))]
664pub fn derive_typed_path(input: TokenStream) -> TokenStream {
665 expand_with(input, typed_path::expand)
666}
667
668/// Derive an implementation of [`FromRef`] for each field in a struct.
669///
670/// # Example
671///
672/// ```
673/// use axum::{
674/// Router,
675/// routing::get,
676/// extract::{State, FromRef},
677/// };
678///
679/// #
680/// # type AuthToken = String;
681/// # type DatabasePool = ();
682/// #
683/// // This will implement `FromRef` for each field in the struct.
684/// #[derive(FromRef, Clone)]
685/// struct AppState {
686/// auth_token: AuthToken,
687/// database_pool: DatabasePool,
688/// // fields can also be skipped
689/// #[from_ref(skip)]
690/// api_token: String,
691/// }
692///
693/// // So those types can be extracted via `State`
694/// async fn handler(State(auth_token): State<AuthToken>) {}
695///
696/// async fn other_handler(State(database_pool): State<DatabasePool>) {}
697///
698/// # let auth_token = Default::default();
699/// # let database_pool = Default::default();
700/// let state = AppState {
701/// auth_token,
702/// database_pool,
703/// api_token: "secret".to_owned(),
704/// };
705///
706/// let app = Router::new()
707/// .route("/", get(handler).post(other_handler))
708/// .with_state(state);
709/// # let _: axum::Router = app;
710/// ```
711///
712/// [`FromRef`]: https://docs.rs/axum/0.8/axum/extract/trait.FromRef.html
713#[proc_macro_derive(FromRef, attributes(from_ref))]
714pub fn derive_from_ref(item: TokenStream) -> TokenStream {
715 expand_with(item, from_ref::expand)
716}
717
718fn expand_with<F, I, K>(input: TokenStream, f: F) -> TokenStream
719where
720 F: FnOnce(I) -> syn::Result<K>,
721 I: Parse,
722 K: ToTokens,
723{
724 expand(syn::parse(input).and_then(f))
725}
726
727fn expand_attr_with<F, A, I, K>(attr: TokenStream, input: TokenStream, f: F) -> TokenStream
728where
729 F: FnOnce(A, I) -> K,
730 A: Parse,
731 I: Parse,
732 K: ToTokens,
733{
734 let expand_result = (|| {
735 let attr = syn::parse(attr)?;
736 let input = syn::parse(input)?;
737 Ok(f(attr, input))
738 })();
739 expand(expand_result)
740}
741
742fn expand<T>(result: syn::Result<T>) -> TokenStream
743where
744 T: ToTokens,
745{
746 match result {
747 Ok(tokens) => {
748 let tokens = (quote! { #tokens }).into();
749 if std::env::var_os("AXUM_MACROS_DEBUG").is_some() {
750 eprintln!("{tokens}");
751 }
752 tokens
753 }
754 Err(err) => err.into_compile_error().into(),
755 }
756}
757
758fn infer_state_types<'a, I>(types: I) -> impl Iterator<Item = Type> + 'a
759where
760 I: Iterator<Item = &'a Type> + 'a,
761{
762 types
763 .filter_map(|ty| {
764 if let Type::Path(path) = ty {
765 Some(&path.path)
766 } else {
767 None
768 }
769 })
770 .filter_map(|path| {
771 if let Some(last_segment) = path.segments.last() {
772 if last_segment.ident != "State" {
773 return None;
774 }
775
776 match &last_segment.arguments {
777 syn::PathArguments::AngleBracketed(args) if args.args.len() == 1 => {
778 Some(args.args.first().unwrap())
779 }
780 _ => None,
781 }
782 } else {
783 None
784 }
785 })
786 .filter_map(|generic_arg| {
787 if let syn::GenericArgument::Type(ty) = generic_arg {
788 Some(ty)
789 } else {
790 None
791 }
792 })
793 .cloned()
794}
795
796#[cfg(test)]
797fn run_ui_tests(directory: &str) {
798 #[rustversion::nightly]
799 fn go(directory: &str) {
800 let t = trybuild::TestCases::new();
801
802 if let Ok(mut path) = std::env::var("AXUM_TEST_ONLY") {
803 if let Some(path_without_prefix) = path.strip_prefix("axum-macros/") {
804 path = path_without_prefix.to_owned();
805 }
806
807 if !path.contains(&format!("/{directory}/")) {
808 return;
809 }
810
811 if path.contains("/fail/") {
812 t.compile_fail(path);
813 } else if path.contains("/pass/") {
814 t.pass(path);
815 } else {
816 panic!()
817 }
818 } else {
819 t.compile_fail(format!("tests/{directory}/fail/*.rs"));
820 t.pass(format!("tests/{directory}/pass/*.rs"));
821 }
822 }
823
824 #[rustversion::not(nightly)]
825 fn go(_directory: &str) {}
826
827 go(directory);
828}