Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
File diff suppressed because one or more lines are too long
+6
View File
@@ -0,0 +1,6 @@
{
"git": {
"sha1": "c59208c86fded335cd85e388030ad59347b0e5ae"
},
"path_in_vcs": "axum-macros"
}
+211
View File
@@ -0,0 +1,211 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
# 0.5.1
- **fixed:** Fix compilation error when deriving `TypedPath` with
`OptionalFromRequestParts` being in scope ([#3645])
[#3645]: https://github.com/tokio-rs/axum/pull/3645
# 0.5.0
*No changes since alpha.1*
## full changelog
- **breaking:** Update code generation for axum-core 0.5.0
- **change:** Update minimum rust version to 1.75 ([#2943])
## alpha.1
- **breaking:** Update code generation for axum-core 0.5.0-alpha.1
- **change:** Update minimum rust version to 1.75 ([#2943])
[#2943]: https://github.com/tokio-rs/axum/pull/2943
# 0.4.2
- **added:** Add `#[debug_middleware]` ([#1993], [#2725])
[#1993]: https://github.com/tokio-rs/axum/pull/1993
[#2725]: https://github.com/tokio-rs/axum/pull/2725
# 0.4.1 (13. January, 2024)
- **fixed:** Improve `debug_handler` on tuple response types ([#2201])
[#2201]: https://github.com/tokio-rs/axum/pull/2201
# 0.4.0 (27. November, 2023)
- **breaking:** `#[debug_handler]` no longer accepts a `body = _` argument. The
body type is always `axum::body::Body` ([#1751])
- **fixed:** Fix `rust-version` specific in Cargo.toml ([#2204])
[#2204]: https://github.com/tokio-rs/axum/pull/2204
[#1751]: https://github.com/tokio-rs/axum/pull/1751
# 0.3.8 (17. July, 2023)
- **fixed:** Allow unreachable code in `#[debug_handler]` ([#2014])
[#2014]: https://github.com/tokio-rs/axum/pull/2014
# 0.3.7 (22. March, 2023)
- **change:** Update to syn 2.0 ([#1862])
- **fixed:** Give better error if generics are used with `#[derive(FromRef)]` ([#1874])
[#1862]: https://github.com/tokio-rs/axum/pull/1862
[#1874]: https://github.com/tokio-rs/axum/pull/1874
# 0.3.6 (13. March, 2023)
- **fixed:** Improve `#[debug_handler]` message for known generic
request-consuming extractors ([#1826])
[#1826]: https://github.com/tokio-rs/axum/pull/1826
# 0.3.5 (03. March, 2023)
- **fixed:** In `#[debug_handler]` provide specific errors about `FromRequest`
extractors not being the last argument ([#1797])
[#1797]: https://github.com/tokio-rs/axum/pull/1797
# 0.3.4 (12. February, 2022)
- **fixed:** Fix `#[derive(FromRef)]` with `Copy` fields generating clippy warnings ([#1749])
[#1749]: https://github.com/tokio-rs/axum/pull/1749
# 0.3.3 (11. February, 2022)
- **fixed:** Fix `#[debug_handler]` sometimes giving wrong borrow related suggestions ([#1710])
[#1710]: https://github.com/tokio-rs/axum/pull/1710
# 0.3.2 (22. January, 2022)
- No public API changes.
# 0.3.1 (9. January, 2022)
- **fixed:** Fix warnings for cloning references in generated code ([#1676])
[#1676]: https://github.com/tokio-rs/axum/pull/1676
# 0.3.0 (25. November, 2022)
- **added:** Add `#[derive(FromRequestParts)]` for deriving an implementation of
`FromRequestParts`, similarly to `#[derive(FromRequest)]` ([#1305])
- **added:** Add `#[derive(FromRef)]` ([#1430])
- **added:** Add `#[from_ref(skip)]` to skip implementing `FromRef` for individual fields ([#1537])
- **added:** Support using a different rejection for `#[derive(FromRequest)]`
with `#[from_request(rejection(MyRejection))]` ([#1256])
- **change:** axum-macro's MSRV is now 1.60 ([#1239])
- **breaking:** `#[derive(FromRequest)]` will no longer generate a rejection
enum but instead generate `type Rejection = axum::response::Response`. Use the
new `#[from_request(rejection(MyRejection))]` attribute to change this.
The `rejection_derive` attribute has also been removed ([#1272])
[#1239]: https://github.com/tokio-rs/axum/pull/1239
[#1256]: https://github.com/tokio-rs/axum/pull/1256
[#1272]: https://github.com/tokio-rs/axum/pull/1272
[#1305]: https://github.com/tokio-rs/axum/pull/1305
[#1430]: https://github.com/tokio-rs/axum/pull/1430
[#1537]: https://github.com/tokio-rs/axum/pull/1537
<details>
<summary>0.3.0 Pre-Releases</summary>
# 0.3.0-rc.3 (18. November, 2022)
- **added:** Add `#[from_ref(skip)]` to skip implementing `FromRef` for individual fields ([#1537])
[#1537]: https://github.com/tokio-rs/axum/pull/1537
# 0.3.0-rc.2 (8. November, 2022)
- **added:** Add `#[derive(FromRef)]` ([#1430])
[#1430]: https://github.com/tokio-rs/axum/pull/1430
# 0.3.0-rc.1 (23. August, 2022)
- **change:** axum-macro's MSRV is now 1.60 ([#1239])
- **added:** Support using a different rejection for `#[derive(FromRequest)]`
with `#[from_request(rejection(MyRejection))]` ([#1256])
- **breaking:** `#[derive(FromRequest)]` will no longer generate a rejection
enum but instead generate `type Rejection = axum::response::Response`. Use the
new `#[from_request(rejection(MyRejection))]` attribute to change this.
The `rejection_derive` attribute has also been removed ([#1272])
- **added:** Add `#[derive(FromRequestParts)]` for deriving an implementation of
`FromRequestParts`, similarly to `#[derive(FromRequest)]` ([#1305])
[#1239]: https://github.com/tokio-rs/axum/pull/1239
[#1256]: https://github.com/tokio-rs/axum/pull/1256
[#1272]: https://github.com/tokio-rs/axum/pull/1272
[#1305]: https://github.com/tokio-rs/axum/pull/1305
</details>
# 0.2.3 (27. June, 2022)
- **change:** axum-macros's MSRV is now 1.56 ([#1098])
- **fixed:** Silence "unnecessary use of `to_string`" lint for `#[derive(TypedPath)]` ([#1117])
[#1098]: https://github.com/tokio-rs/axum/pull/1098
[#1117]: https://github.com/tokio-rs/axum/pull/1117
# 0.2.2 (18. May, 2022)
- **added:** In `debug_handler`, check if `Request` is used as non-final extractor ([#1035])
- **added:** In `debug_handler`, check if multiple `Path` extractors are used ([#1035])
- **added:** In `debug_handler`, check if multiple body extractors are used ([#1036])
- **added:** Support customizing rejections for `#[derive(TypedPath)]` ([#1012])
[#1035]: https://github.com/tokio-rs/axum/pull/1035
[#1036]: https://github.com/tokio-rs/axum/pull/1036
[#1012]: https://github.com/tokio-rs/axum/pull/1012
# 0.2.1 (10. May, 2022)
- **fixed:** `Option` and `Result` are now supported in typed path route handler parameters ([#1001])
- **fixed:** Support wildcards in typed paths ([#1003])
- **added:** Support `#[derive(FromRequest)]` on enums using `#[from_request(via(OtherExtractor))]` ([#1009])
- **added:** Support using a custom rejection type for `#[derive(TypedPath)]`
instead of `PathRejection` ([#1012])
[#1001]: https://github.com/tokio-rs/axum/pull/1001
[#1003]: https://github.com/tokio-rs/axum/pull/1003
[#1009]: https://github.com/tokio-rs/axum/pull/1009
[#1012]: https://github.com/tokio-rs/axum/pull/1012
# 0.2.0 (31. March, 2022)
- **breaking:** Routes are now required to start with `/`. Previously empty routes or routes such
as `:foo` would be accepted but most likely result in bugs ([#823])
[#823]: https://github.com/tokio-rs/axum/pull/823
# 0.1.2 (1. March 2022)
- **fixed:** Use fully qualified `Result` type ([#796])
[#796]: https://github.com/tokio-rs/axum/pull/796
# 0.1.1 (22. February 2022)
- Add `#[derive(TypedPath)]` for use with axum-extra's new "type safe" routing API ([#756])
[#756]: https://github.com/tokio-rs/axum/pull/756
# 0.1.0 (31. January, 2022)
- Initial release.
+562
View File
@@ -0,0 +1,562 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "axum-macros"
version = "0.5.1"
dependencies = [
"proc-macro2",
"quote",
"rustversion",
"serde",
"serde_json",
"syn",
"tokio",
"trybuild",
]
[[package]]
name = "backtrace"
version = "0.3.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-link",
]
[[package]]
name = "bitflags"
version = "2.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
[[package]]
name = "bytes"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
name = "cfg-if"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "gimli"
version = "0.32.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "hashbrown"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
[[package]]
name = "indexmap"
version = "2.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "io-uring"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
dependencies = [
"bitflags",
"cfg-if",
"libc",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "libc"
version = "0.2.176"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
[[package]]
name = "lock_api"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
dependencies = [
"libc",
"wasi",
"windows-sys 0.59.0",
]
[[package]]
name = "object"
version = "0.37.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
dependencies = [
"memchr",
]
[[package]]
name = "parking_lot"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "proc-macro2"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.5.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
dependencies = [
"bitflags",
]
[[package]]
name = "rustc-demangle"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
"serde_core",
]
[[package]]
name = "serde_spanned"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee"
dependencies = [
"serde_core",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "socket2"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]]
name = "syn"
version = "2.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "target-triple"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac9aa371f599d22256307c24a9d748c041e548cbf599f35d890f9d365361790"
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]]
name = "tokio"
version = "1.47.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
dependencies = [
"backtrace",
"bytes",
"io-uring",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"slab",
"socket2",
"tokio-macros",
"windows-sys 0.59.0",
]
[[package]]
name = "tokio-macros"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml"
version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0"
dependencies = [
"indexmap",
"serde_core",
"serde_spanned",
"toml_datetime",
"toml_parser",
"toml_writer",
"winnow",
]
[[package]]
name = "toml_datetime"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_parser"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627"
dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109"
[[package]]
name = "trybuild"
version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ded9fdb81f30a5708920310bfcd9ea7482ff9cba5f54601f7a19a877d5c2392"
dependencies = [
"glob",
"serde",
"serde_derive",
"serde_json",
"target-triple",
"termcolor",
"toml",
]
[[package]]
name = "unicode-ident"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "winapi-util"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.1",
]
[[package]]
name = "windows-link"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.61.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
+127
View File
@@ -0,0 +1,127 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.80"
name = "axum-macros"
version = "0.5.1"
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Macros for axum"
homepage = "https://github.com/tokio-rs/axum"
readme = "README.md"
keywords = ["axum"]
categories = [
"asynchronous",
"network-programming",
"web-programming",
]
license = "MIT"
repository = "https://github.com/tokio-rs/axum"
[package.metadata.cargo-public-api-crates]
allowed = []
[package.metadata.docs.rs]
all-features = true
[features]
__private = ["syn/visit-mut"]
default = []
[lib]
name = "axum_macros"
path = "src/lib.rs"
proc-macro = true
[dependencies.proc-macro2]
version = "1.0"
[dependencies.quote]
version = "1.0"
[dependencies.syn]
version = "2.0"
features = [
"full",
"parsing",
"extra-traits",
]
[dev-dependencies.rustversion]
version = "1.0"
[dev-dependencies.serde]
version = "1.0"
features = ["derive"]
[dev-dependencies.serde_json]
version = "1.0"
[dev-dependencies.syn]
version = "2.0"
features = [
"full",
"extra-traits",
]
[dev-dependencies.tokio]
version = "1.25.0"
features = ["full"]
[dev-dependencies.trybuild]
version = "1.0.63"
[lints.clippy]
await_holding_lock = "warn"
dbg_macro = "warn"
empty_enums = "warn"
enum_glob_use = "warn"
exit = "warn"
filter_map_next = "warn"
fn_params_excessive_bools = "warn"
if_let_mutex = "warn"
imprecise_flops = "warn"
inefficient_to_string = "warn"
linkedlist = "warn"
lossy_float_literal = "warn"
macro_use_imports = "warn"
match_wildcard_for_single_variants = "warn"
mem_forget = "warn"
must_use_candidate = "warn"
needless_borrow = "warn"
needless_continue = "warn"
option_option = "warn"
rest_pat_in_fully_bound_structs = "warn"
return_self_not_must_use = "warn"
str_to_string = "warn"
suboptimal_flops = "warn"
todo = "warn"
type_complexity = "allow"
uninlined_format_args = "warn"
unnested_or_patterns = "warn"
unused_self = "warn"
verbose_file_reads = "warn"
[lints.rust]
missing_debug_implementations = "warn"
missing_docs = "warn"
unreachable_pub = "warn"
unsafe_code = "forbid"
[lints.rust.rust_2018_idioms]
level = "warn"
priority = -1
+48
View File
@@ -0,0 +1,48 @@
[package]
categories = ["asynchronous", "network-programming", "web-programming"]
description = "Macros for axum"
edition = "2021"
rust-version = { workspace = true }
homepage = "https://github.com/tokio-rs/axum"
keywords = ["axum"]
license = "MIT"
name = "axum-macros"
readme = "README.md"
repository = "https://github.com/tokio-rs/axum"
version = "0.5.1" # remember to also bump the version that axum and axum-extra depends on
[features]
default = []
__private = ["syn/visit-mut"]
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "2.0", features = [
"full",
"parsing",
# needed for `Hash` impls
"extra-traits",
] }
[dev-dependencies]
axum = { path = "../axum", features = ["macros"] }
axum-extra = { path = "../axum-extra", features = ["typed-routing", "cookie-private", "typed-header", "routing"] }
rustversion = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
syn = { version = "2.0", features = ["full", "extra-traits"] }
tokio = { version = "1.25.0", features = ["full"] }
trybuild = "1.0.63"
[lints]
workspace = true
[package.metadata.cargo-public-api-crates]
allowed = []
[package.metadata.docs.rs]
all-features = true
+7
View File
@@ -0,0 +1,7 @@
Copyright 2021 axum Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+45
View File
@@ -0,0 +1,45 @@
# axum-macros
[![Build status](https://github.com/tokio-rs/axum/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/tokio-rs/axum-macros/actions/workflows/CI.yml)
[![Crates.io](https://img.shields.io/crates/v/axum-macros)](https://crates.io/crates/axum-macros)
[![Documentation](https://docs.rs/axum-macros/badge.svg)](https://docs.rs/axum-macros)
Macros for [`axum`].
More information about this crate can be found in the [crate documentation][docs].
## Safety
This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in 100% safe Rust.
## Minimum supported Rust version
axum-macros's MSRV is 1.75.
## Getting Help
You're also welcome to ask in the [Discord channel][chat] or open an [issue]
with your question.
## Contributing
🎈 Thanks for your help improving the project! We are so happy to have
you! We have a [contributing guide][contributing] to help you get involved in the
`axum` project.
## License
This project is licensed under the [MIT license][license].
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in `axum` by you, shall be licensed as MIT, without any
additional terms or conditions.
[`axum`]: https://crates.io/crates/axum
[chat]: https://discord.gg/tokio
[contributing]: /CONTRIBUTING.md
[docs]: https://docs.rs/axum-macros
[license]: /axum-macros/LICENSE
[issue]: https://github.com/tokio-rs/axum/issues/new
+1
View File
@@ -0,0 +1 @@
nightly-2024-06-22
+102
View File
@@ -0,0 +1,102 @@
use quote::ToTokens;
use syn::{
parse::{Parse, ParseStream},
Token,
};
pub(crate) fn parse_parenthesized_attribute<K, T>(
input: ParseStream<'_>,
out: &mut Option<(K, T)>,
) -> syn::Result<()>
where
K: Parse + ToTokens,
T: Parse,
{
let kw = input.parse()?;
let content;
syn::parenthesized!(content in input);
let inner = content.parse()?;
if out.is_some() {
let kw_name = std::any::type_name::<K>().split("::").last().unwrap();
let msg = format!("`{kw_name}` specified more than once");
return Err(syn::Error::new_spanned(kw, msg));
}
*out = Some((kw, inner));
Ok(())
}
pub(crate) fn parse_assignment_attribute<K, T>(
input: ParseStream<'_>,
out: &mut Option<(K, T)>,
) -> syn::Result<()>
where
K: Parse + ToTokens,
T: Parse,
{
let kw = input.parse()?;
input.parse::<Token![=]>()?;
let inner = input.parse()?;
if out.is_some() {
let kw_name = std::any::type_name::<K>().split("::").last().unwrap();
let msg = format!("`{kw_name}` specified more than once");
return Err(syn::Error::new_spanned(kw, msg));
}
*out = Some((kw, inner));
Ok(())
}
pub(crate) trait Combine: Sized {
fn combine(self, other: Self) -> syn::Result<Self>;
}
pub(crate) fn parse_attrs<T>(ident: &str, attrs: &[syn::Attribute]) -> syn::Result<T>
where
T: Combine + Default + Parse,
{
attrs
.iter()
.filter(|attr| attr.meta.path().is_ident(ident))
.map(|attr| attr.parse_args::<T>())
.try_fold(T::default(), |out, next| out.combine(next?))
}
pub(crate) fn combine_attribute<K, T>(a: &mut Option<(K, T)>, b: Option<(K, T)>) -> syn::Result<()>
where
K: ToTokens,
{
if let Some((kw, inner)) = b {
if a.is_some() {
let kw_name = std::any::type_name::<K>().split("::").last().unwrap();
let msg = format!("`{kw_name}` specified more than once");
return Err(syn::Error::new_spanned(kw, msg));
}
*a = Some((kw, inner));
}
Ok(())
}
pub(crate) fn combine_unary_attribute<K>(a: &mut Option<K>, b: Option<K>) -> syn::Result<()>
where
K: ToTokens,
{
if let Some(kw) = b {
if a.is_some() {
let kw_name = std::any::type_name::<K>().split("::").last().unwrap();
let msg = format!("`{kw_name}` specified more than once");
return Err(syn::Error::new_spanned(kw, msg));
}
*a = Some(kw);
}
Ok(())
}
pub(crate) fn second<T, K>(tuple: (T, K)) -> K {
tuple.1
}
+45
View File
@@ -0,0 +1,45 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{parse::Parse, parse_quote, visit_mut::VisitMut, ItemFn};
pub(crate) fn expand(_attr: Attrs, mut item_fn: ItemFn) -> TokenStream {
item_fn.attrs.push(parse_quote!(#[tokio::test]));
let nest_service_fn = replace_nest_with_nest_service(item_fn.clone());
quote! {
#item_fn
#nest_service_fn
}
}
pub(crate) struct Attrs;
impl Parse for Attrs {
fn parse(_input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
Ok(Self)
}
}
fn replace_nest_with_nest_service(mut item_fn: ItemFn) -> Option<ItemFn> {
item_fn.sig.ident = format_ident!("{}_with_nest_service", item_fn.sig.ident);
let mut visitor = NestToNestService::default();
syn::visit_mut::visit_item_fn_mut(&mut visitor, &mut item_fn);
(visitor.count > 0).then_some(item_fn)
}
#[derive(Default)]
struct NestToNestService {
count: usize,
}
impl VisitMut for NestToNestService {
fn visit_expr_method_call_mut(&mut self, i: &mut syn::ExprMethodCall) {
if i.method == "nest" && i.args.len() == 2 {
i.method = parse_quote!(nest_service);
self.count += 1;
}
}
}
+891
View File
@@ -0,0 +1,891 @@
use std::{collections::HashSet, fmt};
use crate::{
attr_parsing::{parse_assignment_attribute, second},
with_position::{Position, WithPosition},
};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote, quote_spanned};
use syn::{parse::Parse, spanned::Spanned, FnArg, ItemFn, ReturnType, Token, Type};
pub(crate) fn expand(attr: Attrs, item_fn: ItemFn, kind: FunctionKind) -> TokenStream {
let Attrs { state_ty } = attr;
let mut state_ty = state_ty.map(second);
let check_extractor_count = check_extractor_count(&item_fn, kind);
let check_path_extractor = check_path_extractor(&item_fn, kind);
let check_output_tuples = check_output_tuples(&item_fn);
let check_output_impls_into_response = if check_output_tuples.is_empty() {
check_output_impls_into_response(&item_fn)
} else {
check_output_tuples
};
// If the function is generic, we can't reliably check its inputs or whether the future it
// returns is `Send`. Skip those checks to avoid unhelpful additional compiler errors.
let check_inputs_and_future_send = if item_fn.sig.generics.params.is_empty() {
let mut err = None;
if state_ty.is_none() {
let state_types_from_args = state_types_from_args(&item_fn);
#[allow(clippy::comparison_chain)]
if state_types_from_args.len() == 1 {
state_ty = state_types_from_args.into_iter().next();
} else if state_types_from_args.len() > 1 {
err = Some(
syn::Error::new(
Span::call_site(),
format!(
"can't infer state type, please add set it explicitly, as in \
`#[axum_macros::debug_{kind}(state = MyStateType)]`"
),
)
.into_compile_error(),
);
}
}
err.unwrap_or_else(|| {
let state_ty = state_ty.unwrap_or_else(|| syn::parse_quote!(()));
let check_future_send = check_future_send(&item_fn, kind);
if let Some(check_input_order) = check_input_order(&item_fn, kind) {
quote! {
#check_input_order
#check_future_send
}
} else {
let check_inputs_impls_from_request =
check_inputs_impls_from_request(&item_fn, state_ty, kind);
quote! {
#check_inputs_impls_from_request
#check_future_send
}
}
})
} else {
syn::Error::new_spanned(
&item_fn.sig.generics,
format!("`#[axum_macros::debug_{kind}]` doesn't support generic functions"),
)
.into_compile_error()
};
let middleware_takes_next_as_last_arg =
matches!(kind, FunctionKind::Middleware).then(|| next_is_last_input(&item_fn));
quote! {
#item_fn
#check_extractor_count
#check_path_extractor
#check_output_impls_into_response
#check_inputs_and_future_send
#middleware_takes_next_as_last_arg
}
}
#[derive(Clone, Copy)]
pub(crate) enum FunctionKind {
Handler,
Middleware,
}
impl fmt::Display for FunctionKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FunctionKind::Handler => f.write_str("handler"),
FunctionKind::Middleware => f.write_str("middleware"),
}
}
}
impl FunctionKind {
fn name_uppercase_plural(&self) -> &'static str {
match self {
FunctionKind::Handler => "Handlers",
FunctionKind::Middleware => "Middleware",
}
}
}
mod kw {
syn::custom_keyword!(body);
syn::custom_keyword!(state);
}
pub(crate) struct Attrs {
state_ty: Option<(kw::state, Type)>,
}
impl Parse for Attrs {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let mut state_ty = None;
while !input.is_empty() {
let lh = input.lookahead1();
if lh.peek(kw::state) {
parse_assignment_attribute(input, &mut state_ty)?;
} else {
return Err(lh.error());
}
let _ = input.parse::<Token![,]>();
}
Ok(Self { state_ty })
}
}
fn check_extractor_count(item_fn: &ItemFn, kind: FunctionKind) -> Option<TokenStream> {
let max_extractors = 16;
let inputs = item_fn
.sig
.inputs
.iter()
.filter(|arg| skip_next_arg(arg, kind))
.count();
if inputs <= max_extractors {
None
} else {
let error_message = format!(
"{} cannot take more than {max_extractors} arguments. \
Use `(a, b): (ExtractorA, ExtractorA)` to further nest extractors",
kind.name_uppercase_plural(),
);
let error = syn::Error::new_spanned(&item_fn.sig.inputs, error_message).to_compile_error();
Some(error)
}
}
fn extractor_idents(
item_fn: &ItemFn,
kind: FunctionKind,
) -> impl Iterator<Item = (usize, &syn::FnArg, &syn::Ident)> {
item_fn
.sig
.inputs
.iter()
.filter(move |arg| skip_next_arg(arg, kind))
.enumerate()
.filter_map(|(idx, fn_arg)| match fn_arg {
FnArg::Receiver(_) => None,
FnArg::Typed(pat_type) => {
if let Type::Path(type_path) = &*pat_type.ty {
type_path
.path
.segments
.last()
.map(|segment| (idx, fn_arg, &segment.ident))
} else {
None
}
}
})
}
fn check_path_extractor(item_fn: &ItemFn, kind: FunctionKind) -> TokenStream {
let path_extractors = extractor_idents(item_fn, kind)
.filter(|(_, _, ident)| *ident == "Path")
.collect::<Vec<_>>();
if path_extractors.len() > 1 {
path_extractors
.into_iter()
.map(|(_, arg, _)| {
syn::Error::new_spanned(
arg,
"Multiple parameters must be extracted with a tuple \
`Path<(_, _)>` or a struct `Path<YourParams>`, not by applying \
multiple `Path<_>` extractors",
)
.to_compile_error()
})
.collect()
} else {
quote! {}
}
}
fn is_self_pat_type(typed: &syn::PatType) -> bool {
let ident = if let syn::Pat::Ident(ident) = &*typed.pat {
&ident.ident
} else {
return false;
};
ident == "self"
}
fn check_inputs_impls_from_request(
item_fn: &ItemFn,
state_ty: Type,
kind: FunctionKind,
) -> TokenStream {
let takes_self = item_fn.sig.inputs.first().is_some_and(|arg| match arg {
FnArg::Receiver(_) => true,
FnArg::Typed(typed) => is_self_pat_type(typed),
});
WithPosition::new(
item_fn
.sig
.inputs
.iter()
.filter(|arg| skip_next_arg(arg, kind)),
)
.enumerate()
.map(|(idx, arg)| {
let must_impl_from_request_parts = match &arg {
Position::First(_) | Position::Middle(_) => true,
Position::Last(_) | Position::Only(_) => false,
};
let arg = arg.into_inner();
let (span, ty) = match arg {
FnArg::Receiver(receiver) => {
if receiver.reference.is_some() {
return syn::Error::new_spanned(
receiver,
"Handlers must only take owned values",
)
.into_compile_error();
}
let span = receiver.span();
(span, syn::parse_quote!(Self))
}
FnArg::Typed(typed) => {
let ty = &typed.ty;
let span = ty.span();
if is_self_pat_type(typed) {
(span, syn::parse_quote!(Self))
} else {
(span, ty.clone())
}
}
};
let consumes_request = request_consuming_type_name(&ty).is_some();
let check_fn = format_ident!(
"__axum_macros_check_{}_{}_from_request_check",
item_fn.sig.ident,
idx,
span = span,
);
let call_check_fn = format_ident!(
"__axum_macros_check_{}_{}_from_request_call_check",
item_fn.sig.ident,
idx,
span = span,
);
let call_check_fn_body = if takes_self {
quote_spanned! {span=>
Self::#check_fn();
}
} else {
quote_spanned! {span=>
#check_fn();
}
};
let check_fn_generics = if must_impl_from_request_parts || consumes_request {
quote! {}
} else {
quote! { <M> }
};
let from_request_bound = if must_impl_from_request_parts {
quote_spanned! {span=>
#ty: ::axum::extract::FromRequestParts<#state_ty> + Send
}
} else if consumes_request {
quote_spanned! {span=>
#ty: ::axum::extract::FromRequest<#state_ty> + Send
}
} else {
quote_spanned! {span=>
#ty: ::axum::extract::FromRequest<#state_ty, M> + Send
}
};
quote_spanned! {span=>
#[allow(warnings)]
#[doc(hidden)]
fn #check_fn #check_fn_generics()
where
#from_request_bound,
{}
// we have to call the function to actually trigger a compile error
// since the function is generic, just defining it is not enough
#[allow(warnings)]
#[doc(hidden)]
fn #call_check_fn()
{
#call_check_fn_body
}
}
})
.collect::<TokenStream>()
}
fn check_output_tuples(item_fn: &ItemFn) -> TokenStream {
let elems = match &item_fn.sig.output {
ReturnType::Type(_, ty) => match &**ty {
Type::Tuple(tuple) => &tuple.elems,
_ => return quote! {},
},
ReturnType::Default => return quote! {},
};
let handler_ident = &item_fn.sig.ident;
match elems.len() {
0 => quote! {},
n if n > 17 => syn::Error::new_spanned(
&item_fn.sig.output,
"Cannot return tuples with more than 17 elements",
)
.to_compile_error(),
_ => WithPosition::new(elems)
.enumerate()
.map(|(idx, arg)| match arg {
Position::First(ty) => match extract_clean_typename(ty).as_deref() {
Some("StatusCode" | "Response") => quote! {},
Some("Parts") => check_is_response_parts(ty, handler_ident, idx),
Some(_) | None => {
if let Some(tn) = well_known_last_response_type(ty) {
syn::Error::new_spanned(
ty,
format!(
"`{tn}` must be the last element \
in a response tuple"
),
)
.to_compile_error()
} else {
check_into_response_parts(ty, handler_ident, idx)
}
}
},
Position::Middle(ty) => {
if let Some(tn) = well_known_last_response_type(ty) {
syn::Error::new_spanned(
ty,
format!("`{tn}` must be the last element in a response tuple"),
)
.to_compile_error()
} else {
check_into_response_parts(ty, handler_ident, idx)
}
}
Position::Last(ty) | Position::Only(ty) => check_into_response(handler_ident, ty),
})
.collect::<TokenStream>(),
}
}
fn check_into_response(handler: &Ident, ty: &Type) -> TokenStream {
let (span, ty) = (ty.span(), ty.clone());
let check_fn = format_ident!(
"__axum_macros_check_{handler}_into_response_check",
span = span,
);
let call_check_fn = format_ident!(
"__axum_macros_check_{handler}_into_response_call_check",
span = span,
);
let call_check_fn_body = quote_spanned! {span=>
#check_fn();
};
let from_request_bound = quote_spanned! {span=>
#ty: ::axum::response::IntoResponse
};
quote_spanned! {span=>
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #check_fn()
where
#from_request_bound,
{}
// we have to call the function to actually trigger a compile error
// since the function is generic, just defining it is not enough
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #call_check_fn() {
#call_check_fn_body
}
}
}
fn check_is_response_parts(ty: &Type, ident: &Ident, index: usize) -> TokenStream {
let (span, ty) = (ty.span(), ty.clone());
let check_fn = format_ident!(
"__axum_macros_check_{}_is_response_parts_{index}_check",
ident,
span = span,
);
quote_spanned! {span=>
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #check_fn(parts: #ty) -> ::axum::http::response::Parts {
parts
}
}
}
fn check_into_response_parts(ty: &Type, ident: &Ident, index: usize) -> TokenStream {
let (span, ty) = (ty.span(), ty.clone());
let check_fn = format_ident!(
"__axum_macros_check_{}_into_response_parts_{index}_check",
ident,
span = span,
);
let call_check_fn = format_ident!(
"__axum_macros_check_{}_into_response_parts_{index}_call_check",
ident,
span = span,
);
let call_check_fn_body = quote_spanned! {span=>
#check_fn();
};
let from_request_bound = quote_spanned! {span=>
#ty: ::axum::response::IntoResponseParts
};
quote_spanned! {span=>
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #check_fn()
where
#from_request_bound,
{}
// we have to call the function to actually trigger a compile error
// since the function is generic, just defining it is not enough
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #call_check_fn() {
#call_check_fn_body
}
}
}
fn check_input_order(item_fn: &ItemFn, kind: FunctionKind) -> Option<TokenStream> {
let number_of_inputs = item_fn
.sig
.inputs
.iter()
.filter(|arg| skip_next_arg(arg, kind))
.count();
let types_that_consume_the_request = item_fn
.sig
.inputs
.iter()
.filter(|arg| skip_next_arg(arg, kind))
.enumerate()
.filter_map(|(idx, arg)| {
let ty = match arg {
FnArg::Typed(pat_type) => &*pat_type.ty,
FnArg::Receiver(_) => return None,
};
let type_name = request_consuming_type_name(ty)?;
Some((idx, type_name, ty.span()))
})
.collect::<Vec<_>>();
if types_that_consume_the_request.is_empty() {
return None;
};
// exactly one type that consumes the request
if types_that_consume_the_request.len() == 1 {
// and that is not the last
if types_that_consume_the_request[0].0 != number_of_inputs - 1 {
let (_idx, type_name, span) = &types_that_consume_the_request[0];
let error = format!(
"`{type_name}` consumes the request body and thus must be \
the last argument to the handler function"
);
return Some(quote_spanned! {*span=>
compile_error!(#error);
});
} else {
return None;
}
}
if types_that_consume_the_request.len() == 2 {
let (_, first, _) = &types_that_consume_the_request[0];
let (_, second, _) = &types_that_consume_the_request[1];
let error = format!(
"Can't have two extractors that consume the request body. \
`{first}` and `{second}` both do that.",
);
let span = item_fn.sig.inputs.span();
Some(quote_spanned! {span=>
compile_error!(#error);
})
} else {
let types = WithPosition::new(types_that_consume_the_request)
.map(|pos| match pos {
Position::First((_, type_name, _)) | Position::Middle((_, type_name, _)) => {
format!("`{type_name}`, ")
}
Position::Last((_, type_name, _)) => format!("and `{type_name}`"),
Position::Only(_) => unreachable!(),
})
.collect::<String>();
let error = format!(
"Can't have more than one extractor that consume the request body. \
{types} all do that.",
);
let span = item_fn.sig.inputs.span();
Some(quote_spanned! {span=>
compile_error!(#error);
})
}
}
fn extract_clean_typename(ty: &Type) -> Option<String> {
let path = match ty {
Type::Path(type_path) => &type_path.path,
_ => return None,
};
path.segments.last().map(|p| p.ident.to_string())
}
fn request_consuming_type_name(ty: &Type) -> Option<&'static str> {
let typename = extract_clean_typename(ty)?;
let type_name = match &*typename {
"Json" => "Json<_>",
"RawBody" => "RawBody<_>",
"RawForm" => "RawForm",
"Multipart" => "Multipart",
"Protobuf" => "Protobuf",
"JsonLines" => "JsonLines<_>",
"Form" => "Form<_>",
"Request" => "Request<_>",
"Bytes" => "Bytes",
"String" => "String",
"Parts" => "Parts",
_ => return None,
};
Some(type_name)
}
fn well_known_last_response_type(ty: &Type) -> Option<&'static str> {
let typename = extract_clean_typename(ty)?;
let type_name = match &*typename {
"Json" => "Json<_>",
"Protobuf" => "Protobuf",
"JsonLines" => "JsonLines<_>",
"Form" => "Form<_>",
"Bytes" => "Bytes",
"String" => "String",
_ => return None,
};
Some(type_name)
}
fn check_output_impls_into_response(item_fn: &ItemFn) -> TokenStream {
let ty = match &item_fn.sig.output {
syn::ReturnType::Default => return quote! {},
syn::ReturnType::Type(_, ty) => ty,
};
let span = ty.span();
let declare_inputs = item_fn
.sig
.inputs
.iter()
.filter_map(|arg| match arg {
FnArg::Receiver(_) => None,
FnArg::Typed(pat_ty) => {
let pat = &pat_ty.pat;
let ty = &pat_ty.ty;
Some(quote! {
let #pat: #ty = panic!();
})
}
})
.collect::<TokenStream>();
let block = &item_fn.block;
let make_value_name = format_ident!(
"__axum_macros_check_{}_into_response_make_value",
item_fn.sig.ident
);
let make = if item_fn.sig.asyncness.is_some() {
quote_spanned! {span=>
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
async fn #make_value_name() -> #ty {
#declare_inputs
#block
}
}
} else {
quote_spanned! {span=>
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #make_value_name() -> #ty {
#declare_inputs
#block
}
}
};
let name = format_ident!("__axum_macros_check_{}_into_response", item_fn.sig.ident);
if let Some(receiver) = self_receiver(item_fn) {
quote_spanned! {span=>
#make
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
async fn #name() {
fn check<T>(_: T)
where T: ::axum::response::IntoResponse
{}
let value = #receiver #make_value_name().await;
check(value);
}
}
} else {
quote_spanned! {span=>
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
async fn #name() {
#make
fn check<T>(_: T)
where T: ::axum::response::IntoResponse
{}
let value = #make_value_name().await;
check(value);
}
}
}
}
fn check_future_send(item_fn: &ItemFn, kind: FunctionKind) -> TokenStream {
if item_fn.sig.asyncness.is_none() {
match &item_fn.sig.output {
syn::ReturnType::Default => {
return syn::Error::new_spanned(
item_fn.sig.fn_token,
format!("{} must be `async fn`s", kind.name_uppercase_plural()),
)
.into_compile_error();
}
syn::ReturnType::Type(_, ty) => ty,
};
}
let span = item_fn.sig.ident.span();
let handler_name = &item_fn.sig.ident;
let args = item_fn.sig.inputs.iter().map(|_| {
quote_spanned! {span=> panic!() }
});
let name = format_ident!("__axum_macros_check_{}_future", item_fn.sig.ident);
let define_check = quote! {
fn check<T>(_: T)
where T: ::std::future::Future + Send
{}
};
let do_check = quote! {
check(future);
};
if let Some(receiver) = self_receiver(item_fn) {
quote! {
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #name() {
#define_check
let future = #receiver #handler_name(#(#args),*);
#do_check
}
}
} else {
quote! {
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #name() {
#item_fn
#define_check
let future = #handler_name(#(#args),*);
#do_check
}
}
}
}
fn self_receiver(item_fn: &ItemFn) -> Option<TokenStream> {
let takes_self = item_fn.sig.inputs.iter().any(|arg| match arg {
FnArg::Receiver(_) => true,
FnArg::Typed(typed) => is_self_pat_type(typed),
});
if takes_self {
return Some(quote! { Self:: });
}
if let syn::ReturnType::Type(_, ty) = &item_fn.sig.output {
if let syn::Type::Path(path) = &**ty {
let segments = &path.path.segments;
if segments.len() == 1 {
if let Some(last) = segments.last() {
match &last.arguments {
syn::PathArguments::None if last.ident == "Self" => {
return Some(quote! { Self:: });
}
_ => {}
}
}
}
}
}
None
}
/// Given a signature like
///
/// ```skip
/// #[debug_handler]
/// async fn handler(
/// _: axum::extract::State<AppState>,
/// _: State<AppState>,
/// ) {}
/// ```
///
/// This will extract `AppState`.
///
/// Returns `None` if there are no `State` args or multiple of different types.
fn state_types_from_args(item_fn: &ItemFn) -> HashSet<Type> {
let types = item_fn
.sig
.inputs
.iter()
.filter_map(|input| match input {
FnArg::Receiver(_) => None,
FnArg::Typed(pat_type) => Some(pat_type),
})
.map(|pat_type| &*pat_type.ty);
crate::infer_state_types(types).collect()
}
fn next_is_last_input(item_fn: &ItemFn) -> TokenStream {
let next_args = item_fn
.sig
.inputs
.iter()
.enumerate()
.filter(|(_, arg)| !skip_next_arg(arg, FunctionKind::Middleware))
.collect::<Vec<_>>();
if next_args.is_empty() {
return quote! {
compile_error!(
"Middleware functions must take `axum::middleware::Next` as the last argument",
);
};
}
if next_args.len() == 1 {
let (idx, arg) = &next_args[0];
if *idx != item_fn.sig.inputs.len() - 1 {
return quote_spanned! {arg.span()=>
compile_error!("`axum::middleware::Next` must the last argument");
};
}
}
if next_args.len() >= 2 {
return quote! {
compile_error!(
"Middleware functions can only take one argument of type `axum::middleware::Next`",
);
};
}
quote! {}
}
fn skip_next_arg(arg: &FnArg, kind: FunctionKind) -> bool {
match kind {
FunctionKind::Handler => true,
FunctionKind::Middleware => match arg {
FnArg::Receiver(_) => true,
FnArg::Typed(pat_type) => {
if let Type::Path(type_path) = &*pat_type.ty {
type_path
.path
.segments
.last()
.map_or(true, |path_segment| path_segment.ident != "Next")
} else {
true
}
}
},
}
}
#[test]
fn ui_debug_handler() {
crate::run_ui_tests("debug_handler");
}
#[test]
fn ui_debug_middleware() {
crate::run_ui_tests("debug_middleware");
}
+105
View File
@@ -0,0 +1,105 @@
use proc_macro2::{Ident, TokenStream};
use quote::quote_spanned;
use syn::{
parse::{Parse, ParseStream},
spanned::Spanned,
Field, ItemStruct, Token, Type,
};
use crate::attr_parsing::{combine_unary_attribute, parse_attrs, Combine};
pub(crate) fn expand(item: ItemStruct) -> syn::Result<TokenStream> {
if !item.generics.params.is_empty() {
return Err(syn::Error::new_spanned(
item.generics,
"`#[derive(FromRef)]` doesn't support generics",
));
}
let tokens = item
.fields
.iter()
.enumerate()
.map(|(idx, field)| expand_field(&item.ident, idx, field))
.collect();
Ok(tokens)
}
fn expand_field(state: &Ident, idx: usize, field: &Field) -> TokenStream {
let FieldAttrs { skip } = match parse_attrs("from_ref", &field.attrs) {
Ok(attrs) => attrs,
Err(err) => return err.into_compile_error(),
};
if skip.is_some() {
return TokenStream::default();
}
let field_ty = &field.ty;
let span = field.ty.span();
let body = if let Some(field_ident) = &field.ident {
if matches!(field_ty, Type::Reference(_)) {
quote_spanned! {span=> state.#field_ident }
} else {
quote_spanned! {span=> state.#field_ident.clone() }
}
} else {
let idx = syn::Index {
index: idx as _,
span: field.span(),
};
quote_spanned! {span=> state.#idx.clone() }
};
quote_spanned! {span=>
#[allow(clippy::clone_on_copy, clippy::clone_on_ref_ptr)]
impl ::axum::extract::FromRef<#state> for #field_ty {
fn from_ref(state: &#state) -> Self {
#body
}
}
}
}
mod kw {
syn::custom_keyword!(skip);
}
#[derive(Default)]
pub(super) struct FieldAttrs {
pub(super) skip: Option<kw::skip>,
}
impl Parse for FieldAttrs {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let mut skip = None;
while !input.is_empty() {
let lh = input.lookahead1();
if lh.peek(kw::skip) {
skip = Some(input.parse()?);
} else {
return Err(lh.error());
}
let _ = input.parse::<Token![,]>();
}
Ok(Self { skip })
}
}
impl Combine for FieldAttrs {
fn combine(mut self, other: Self) -> syn::Result<Self> {
let Self { skip } = other;
combine_unary_attribute(&mut self.skip, skip)?;
Ok(self)
}
}
#[test]
fn ui() {
crate::run_ui_tests("from_ref");
}
@@ -0,0 +1,93 @@
use crate::attr_parsing::{combine_attribute, parse_parenthesized_attribute, Combine};
use syn::{
parse::{Parse, ParseStream},
Token,
};
pub(crate) mod kw {
syn::custom_keyword!(via);
syn::custom_keyword!(rejection);
syn::custom_keyword!(state);
}
#[derive(Default)]
pub(super) struct FromRequestContainerAttrs {
pub(super) via: Option<(kw::via, syn::Path)>,
pub(super) rejection: Option<(kw::rejection, syn::Path)>,
pub(super) state: Option<(kw::state, syn::Type)>,
}
impl Parse for FromRequestContainerAttrs {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let mut via = None;
let mut rejection = None;
let mut state = None;
while !input.is_empty() {
let lh = input.lookahead1();
if lh.peek(kw::via) {
parse_parenthesized_attribute(input, &mut via)?;
} else if lh.peek(kw::rejection) {
parse_parenthesized_attribute(input, &mut rejection)?;
} else if lh.peek(kw::state) {
parse_parenthesized_attribute(input, &mut state)?;
} else {
return Err(lh.error());
}
let _ = input.parse::<Token![,]>();
}
Ok(Self {
via,
rejection,
state,
})
}
}
impl Combine for FromRequestContainerAttrs {
fn combine(mut self, other: Self) -> syn::Result<Self> {
let Self {
via,
rejection,
state,
} = other;
combine_attribute(&mut self.via, via)?;
combine_attribute(&mut self.rejection, rejection)?;
combine_attribute(&mut self.state, state)?;
Ok(self)
}
}
#[derive(Default)]
pub(super) struct FromRequestFieldAttrs {
pub(super) via: Option<(kw::via, syn::Path)>,
}
impl Parse for FromRequestFieldAttrs {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let mut via = None;
while !input.is_empty() {
let lh = input.lookahead1();
if lh.peek(kw::via) {
parse_parenthesized_attribute(input, &mut via)?;
} else {
return Err(lh.error());
}
let _ = input.parse::<Token![,]>();
}
Ok(Self { via })
}
}
impl Combine for FromRequestFieldAttrs {
fn combine(mut self, other: Self) -> syn::Result<Self> {
let Self { via } = other;
combine_attribute(&mut self.via, via)?;
Ok(self)
}
}
File diff suppressed because it is too large Load Diff
+828
View File
@@ -0,0 +1,828 @@
//! Macros for [`axum`].
//!
//! [`axum`]: https://crates.io/crates/axum
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(test, allow(clippy::float_cmp))]
#![cfg_attr(not(test), warn(clippy::print_stdout, clippy::dbg_macro))]
use debug_handler::FunctionKind;
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse::Parse, Type};
mod attr_parsing;
#[cfg(feature = "__private")]
mod axum_test;
mod debug_handler;
mod from_ref;
mod from_request;
mod typed_path;
mod with_position;
use from_request::Trait::{FromRequest, FromRequestParts};
/// Derive an implementation of [`FromRequest`].
///
/// Supports generating two kinds of implementations:
/// 1. One that extracts each field individually.
/// 2. Another that extracts the whole type at once via another extractor.
///
/// # Each field individually
///
/// By default `#[derive(FromRequest)]` will call `FromRequest::from_request` for each field:
///
/// ```
/// use axum_macros::FromRequest;
/// use axum::{
/// extract::Extension,
/// body::Bytes,
/// };
/// use axum_extra::{
/// TypedHeader,
/// headers::ContentType,
/// };
///
/// #[derive(FromRequest)]
/// struct MyExtractor {
/// state: Extension<State>,
/// content_type: TypedHeader<ContentType>,
/// request_body: Bytes,
/// }
///
/// #[derive(Clone)]
/// struct State {
/// // ...
/// }
///
/// async fn handler(extractor: MyExtractor) {}
/// ```
///
/// This requires that each field is an extractor (i.e. implements [`FromRequest`]).
///
/// Note that only the last field can consume the request body. Therefore this doesn't compile:
///
/// ```compile_fail
/// use axum_macros::FromRequest;
/// use axum::body::Bytes;
///
/// #[derive(FromRequest)]
/// struct MyExtractor {
/// // only the last field can implement `FromRequest`
/// // other fields must only implement `FromRequestParts`
/// bytes: Bytes,
/// string: String,
/// }
/// ```
///
/// ## Extracting via another extractor
///
/// You can use `#[from_request(via(...))]` to extract a field via another extractor, meaning the
/// field itself doesn't need to implement `FromRequest`:
///
/// ```
/// use axum_macros::FromRequest;
/// use axum::{
/// extract::Extension,
/// body::Bytes,
/// };
/// use axum_extra::{
/// TypedHeader,
/// headers::ContentType,
/// };
///
/// #[derive(FromRequest)]
/// struct MyExtractor {
/// // This will extracted via `Extension::<State>::from_request`
/// #[from_request(via(Extension))]
/// state: State,
/// // and this via `TypedHeader::<ContentType>::from_request`
/// #[from_request(via(TypedHeader))]
/// content_type: ContentType,
/// // Can still be combined with other extractors
/// request_body: Bytes,
/// }
///
/// #[derive(Clone)]
/// struct State {
/// // ...
/// }
///
/// async fn handler(extractor: MyExtractor) {}
/// ```
///
/// Note this requires the via extractor to be a generic newtype struct (a tuple struct with
/// exactly one public field) that implements `FromRequest`:
///
/// ```
/// pub struct ViaExtractor<T>(pub T);
///
/// // impl<T, S> FromRequest<S> for ViaExtractor<T> { ... }
/// ```
///
/// More complex via extractors are not supported and require writing a manual implementation.
///
/// ## Optional fields
///
/// `#[from_request(via(...))]` supports `Option<_>` and `Result<_, _>` to make fields optional:
///
/// ```
/// use axum_macros::FromRequest;
/// use axum_extra::{
/// TypedHeader,
/// headers::{ContentType, UserAgent},
/// typed_header::TypedHeaderRejection,
/// };
///
/// #[derive(FromRequest)]
/// struct MyExtractor {
/// // This will extracted via `Option::<TypedHeader<ContentType>>::from_request`
/// #[from_request(via(TypedHeader))]
/// content_type: Option<ContentType>,
/// // This will extracted via
/// // `Result::<TypedHeader<UserAgent>, TypedHeaderRejection>::from_request`
/// #[from_request(via(TypedHeader))]
/// user_agent: Result<UserAgent, TypedHeaderRejection>,
/// }
///
/// async fn handler(extractor: MyExtractor) {}
/// ```
///
/// ## The rejection
///
/// By default [`axum::response::Response`] will be used as the rejection. You can also use your own
/// rejection type with `#[from_request(rejection(YourType))]`:
///
/// ```
/// use axum::{
/// extract::{
/// rejection::{ExtensionRejection, StringRejection},
/// FromRequest,
/// },
/// Extension,
/// response::{Response, IntoResponse},
/// };
///
/// #[derive(FromRequest)]
/// #[from_request(rejection(MyRejection))]
/// struct MyExtractor {
/// state: Extension<String>,
/// body: String,
/// }
///
/// struct MyRejection(Response);
///
/// // This tells axum how to convert `Extension`'s rejections into `MyRejection`
/// impl From<ExtensionRejection> for MyRejection {
/// fn from(rejection: ExtensionRejection) -> Self {
/// // ...
/// # todo!()
/// }
/// }
///
/// // This tells axum how to convert `String`'s rejections into `MyRejection`
/// impl From<StringRejection> for MyRejection {
/// fn from(rejection: StringRejection) -> Self {
/// // ...
/// # todo!()
/// }
/// }
///
/// // All rejections must implement `IntoResponse`
/// impl IntoResponse for MyRejection {
/// fn into_response(self) -> Response {
/// self.0
/// }
/// }
/// ```
///
/// ## Concrete state
///
/// If the extraction can be done only for a concrete state, that type can be specified with
/// `#[from_request(state(YourState))]`:
///
/// ```
/// use axum::extract::{FromRequest, FromRequestParts};
///
/// #[derive(Clone)]
/// struct CustomState;
///
/// struct MyInnerType;
///
/// impl FromRequestParts<CustomState> for MyInnerType {
/// // ...
/// # type Rejection = ();
///
/// # async fn from_request_parts(
/// # _parts: &mut axum::http::request::Parts,
/// # _state: &CustomState
/// # ) -> Result<Self, Self::Rejection> {
/// # todo!()
/// # }
/// }
///
/// #[derive(FromRequest)]
/// #[from_request(state(CustomState))]
/// struct MyExtractor {
/// custom: MyInnerType,
/// body: String,
/// }
/// ```
///
/// This is not needed for a `State<T>` as the type is inferred in that case.
///
/// ```
/// use axum::extract::{FromRequest, FromRequestParts, State};
///
/// #[derive(Clone)]
/// struct CustomState;
///
/// #[derive(FromRequest)]
/// struct MyExtractor {
/// custom: State<CustomState>,
/// body: String,
/// }
/// ```
///
/// # The whole type at once
///
/// By using `#[from_request(via(...))]` on the container you can extract the whole type at once,
/// instead of each field individually:
///
/// ```
/// use axum_macros::FromRequest;
/// use axum::extract::Extension;
///
/// // This will extracted via `Extension::<State>::from_request`
/// #[derive(Clone, FromRequest)]
/// #[from_request(via(Extension))]
/// struct State {
/// // ...
/// }
///
/// async fn handler(state: State) {}
/// ```
///
/// The rejection will be the "via extractors"'s rejection. For the previous example that would be
/// [`axum::extract::rejection::ExtensionRejection`].
///
/// You can use a different rejection type with `#[from_request(rejection(YourType))]`:
///
/// ```
/// use axum_macros::FromRequest;
/// use axum::{
/// extract::{Extension, rejection::ExtensionRejection},
/// response::{IntoResponse, Response},
/// Json,
/// http::StatusCode,
/// };
/// use serde_json::json;
///
/// // This will extracted via `Extension::<State>::from_request`
/// #[derive(Clone, FromRequest)]
/// #[from_request(
/// via(Extension),
/// // Use your own rejection type
/// rejection(MyRejection),
/// )]
/// struct State {
/// // ...
/// }
///
/// struct MyRejection(Response);
///
/// // This tells axum how to convert `Extension`'s rejections into `MyRejection`
/// impl From<ExtensionRejection> for MyRejection {
/// fn from(rejection: ExtensionRejection) -> Self {
/// let response = (
/// StatusCode::INTERNAL_SERVER_ERROR,
/// Json(json!({ "error": "Something went wrong..." })),
/// ).into_response();
///
/// MyRejection(response)
/// }
/// }
///
/// // All rejections must implement `IntoResponse`
/// impl IntoResponse for MyRejection {
/// fn into_response(self) -> Response {
/// self.0
/// }
/// }
///
/// async fn handler(state: State) {}
/// ```
///
/// This allows you to wrap other extractors and easily customize the rejection:
///
/// ```
/// use axum_macros::FromRequest;
/// use axum::{
/// extract::{Extension, rejection::JsonRejection},
/// response::{IntoResponse, Response},
/// http::StatusCode,
/// };
/// use serde_json::json;
/// use serde::Deserialize;
///
/// // create an extractor that internally uses `axum::Json` but has a custom rejection
/// #[derive(FromRequest)]
/// #[from_request(via(axum::Json), rejection(MyRejection))]
/// struct MyJson<T>(T);
///
/// struct MyRejection(Response);
///
/// impl From<JsonRejection> for MyRejection {
/// fn from(rejection: JsonRejection) -> Self {
/// let response = (
/// StatusCode::INTERNAL_SERVER_ERROR,
/// axum::Json(json!({ "error": rejection.to_string() })),
/// ).into_response();
///
/// MyRejection(response)
/// }
/// }
///
/// impl IntoResponse for MyRejection {
/// fn into_response(self) -> Response {
/// self.0
/// }
/// }
///
/// #[derive(Deserialize)]
/// struct Payload {}
///
/// async fn handler(
/// // make sure to use `MyJson` and not `axum::Json`
/// MyJson(payload): MyJson<Payload>,
/// ) {}
/// ```
///
/// # Known limitations
///
/// Generics are only supported on tuple structs with exactly one field. Thus this doesn't work
///
/// ```compile_fail
/// #[derive(axum_macros::FromRequest)]
/// struct MyExtractor<T> {
/// thing: Option<T>,
/// }
/// ```
///
/// [`FromRequest`]: https://docs.rs/axum/0.8/axum/extract/trait.FromRequest.html
/// [`axum::response::Response`]: https://docs.rs/axum/0.8/axum/response/type.Response.html
/// [`axum::extract::rejection::ExtensionRejection`]: https://docs.rs/axum/0.8/axum/extract/rejection/enum.ExtensionRejection.html
#[proc_macro_derive(FromRequest, attributes(from_request))]
pub fn derive_from_request(item: TokenStream) -> TokenStream {
expand_with(item, |item| from_request::expand(item, FromRequest))
}
/// Derive an implementation of [`FromRequestParts`].
///
/// This works similarly to `#[derive(FromRequest)]` except it uses [`FromRequestParts`]. All the
/// same options are supported.
///
/// # Example
///
/// ```
/// use axum_macros::FromRequestParts;
/// use axum::{
/// extract::Query,
/// };
/// use axum_extra::{
/// TypedHeader,
/// headers::ContentType,
/// };
/// use std::collections::HashMap;
///
/// #[derive(FromRequestParts)]
/// struct MyExtractor {
/// #[from_request(via(Query))]
/// query_params: HashMap<String, String>,
/// content_type: TypedHeader<ContentType>,
/// }
///
/// async fn handler(extractor: MyExtractor) {}
/// ```
///
/// # Cannot extract the body
///
/// [`FromRequestParts`] cannot extract the request body:
///
/// ```compile_fail
/// use axum_macros::FromRequestParts;
///
/// #[derive(FromRequestParts)]
/// struct MyExtractor {
/// body: String,
/// }
/// ```
///
/// Use `#[derive(FromRequest)]` for that.
///
/// [`FromRequestParts`]: https://docs.rs/axum/0.8/axum/extract/trait.FromRequestParts.html
#[proc_macro_derive(FromRequestParts, attributes(from_request))]
pub fn derive_from_request_parts(item: TokenStream) -> TokenStream {
expand_with(item, |item| from_request::expand(item, FromRequestParts))
}
/// Generates better error messages when applied to handler functions.
///
/// While using [`axum`], you can get long error messages for simple mistakes. For example:
///
/// ```compile_fail
/// use axum::{routing::get, Router};
///
/// #[tokio::main]
/// async fn main() {
/// let app = Router::new().route("/", get(handler));
///
/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
/// axum::serve(listener, app).await.unwrap();
/// }
///
/// fn handler() -> &'static str {
/// "Hello, world"
/// }
/// ```
///
/// You will get a long error message about function not implementing [`Handler`] trait. But why
/// does this function not implement it? To figure it out, the [`debug_handler`] macro can be used.
///
/// ```compile_fail
/// # use axum::{routing::get, Router};
/// # use axum_macros::debug_handler;
/// #
/// # #[tokio::main]
/// # async fn main() {
/// # let app = Router::new().route("/", get(handler));
/// #
/// # let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
/// # axum::serve(listener, app).await.unwrap();
/// # }
/// #
/// #[debug_handler]
/// fn handler() -> &'static str {
/// "Hello, world"
/// }
/// ```
///
/// ```text
/// error: handlers must be async functions
/// --> main.rs:xx:1
/// |
/// xx | fn handler() -> &'static str {
/// | ^^
/// ```
///
/// As the error message says, handler function needs to be async.
///
/// ```no_run
/// use axum::{routing::get, Router, debug_handler};
///
/// #[tokio::main]
/// async fn main() {
/// let app = Router::new().route("/", get(handler));
///
/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
/// axum::serve(listener, app).await.unwrap();
/// }
///
/// #[debug_handler]
/// async fn handler() -> &'static str {
/// "Hello, world"
/// }
/// ```
///
/// # Changing state type
///
/// By default `#[debug_handler]` assumes your state type is `()` unless your handler has a
/// [`axum::extract::State`] argument:
///
/// ```
/// use axum::{debug_handler, extract::State};
///
/// #[debug_handler]
/// async fn handler(
/// // this makes `#[debug_handler]` use `AppState`
/// State(state): State<AppState>,
/// ) {}
///
/// #[derive(Clone)]
/// struct AppState {}
/// ```
///
/// If your handler takes multiple [`axum::extract::State`] arguments or you need to otherwise
/// customize the state type you can set it with `#[debug_handler(state = ...)]`:
///
/// ```
/// use axum::{debug_handler, extract::{State, FromRef}};
///
/// #[debug_handler(state = AppState)]
/// async fn handler(
/// State(app_state): State<AppState>,
/// State(inner_state): State<InnerState>,
/// ) {}
///
/// #[derive(Clone)]
/// struct AppState {
/// inner: InnerState,
/// }
///
/// #[derive(Clone)]
/// struct InnerState {}
///
/// impl FromRef<AppState> for InnerState {
/// fn from_ref(state: &AppState) -> Self {
/// state.inner.clone()
/// }
/// }
/// ```
///
/// # Limitations
///
/// This macro does not work for functions in an `impl` block that don't have a `self` parameter:
///
/// ```compile_fail
/// use axum::{debug_handler, extract::Path};
///
/// struct App {}
///
/// impl App {
/// #[debug_handler]
/// async fn handler(Path(_): Path<String>) {}
/// }
/// ```
///
/// This will yield an error similar to this:
///
/// ```text
/// error[E0425]: cannot find function `__axum_macros_check_handler_0_from_request_check` in this scope
// --> src/main.rs:xx:xx
// |
// xx | pub async fn handler(Path(_): Path<String>) {}
// | ^^^^ not found in this scope
/// ```
///
/// # Performance
///
/// This macro has no effect when compiled with the release profile. (eg. `cargo build --release`)
///
/// [`axum`]: https://docs.rs/axum/0.8
/// [`Handler`]: https://docs.rs/axum/0.8/axum/handler/trait.Handler.html
/// [`axum::extract::State`]: https://docs.rs/axum/0.8/axum/extract/struct.State.html
/// [`debug_handler`]: macro@debug_handler
#[proc_macro_attribute]
pub fn debug_handler(_attr: TokenStream, input: TokenStream) -> TokenStream {
#[cfg(not(debug_assertions))]
return input;
#[cfg(debug_assertions)]
return expand_attr_with(_attr, input, |attrs, item_fn| {
debug_handler::expand(attrs, item_fn, FunctionKind::Handler)
});
}
/// Generates better error messages when applied to middleware functions.
///
/// This works similarly to [`#[debug_handler]`](macro@debug_handler) except for middleware using
/// [`axum::middleware::from_fn`].
///
/// # Example
///
/// ```no_run
/// use axum::{
/// routing::get,
/// extract::Request,
/// response::Response,
/// Router,
/// middleware::{self, Next},
/// debug_middleware,
/// };
///
/// #[tokio::main]
/// async fn main() {
/// let app = Router::new()
/// .route("/", get(|| async {}))
/// .layer(middleware::from_fn(my_middleware));
///
/// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
/// axum::serve(listener, app).await.unwrap();
/// }
///
/// // if this wasn't a valid middleware function #[debug_middleware] would
/// // improve compile error
/// #[debug_middleware]
/// async fn my_middleware(
/// request: Request,
/// next: Next,
/// ) -> Response {
/// next.run(request).await
/// }
/// ```
///
/// # Performance
///
/// This macro has no effect when compiled with the release profile. (eg. `cargo build --release`)
///
/// [`axum`]: https://docs.rs/axum/latest
/// [`axum::middleware::from_fn`]: https://docs.rs/axum/0.8/axum/middleware/fn.from_fn.html
/// [`debug_middleware`]: macro@debug_middleware
#[proc_macro_attribute]
pub fn debug_middleware(_attr: TokenStream, input: TokenStream) -> TokenStream {
#[cfg(not(debug_assertions))]
return input;
#[cfg(debug_assertions)]
return expand_attr_with(_attr, input, |attrs, item_fn| {
debug_handler::expand(attrs, item_fn, FunctionKind::Middleware)
});
}
/// Private API: Do no use this!
///
/// Attribute macro to be placed on test functions that'll generate two functions:
///
/// 1. One identical to the function it was placed on.
/// 2. One where calls to `Router::nest` has been replaced with `Router::nest_service`
///
/// This makes it easy to that `nest` and `nest_service` behaves in the same way, without having to
/// manually write identical tests for both methods.
#[cfg(feature = "__private")]
#[proc_macro_attribute]
#[doc(hidden)]
pub fn __private_axum_test(_attr: TokenStream, input: TokenStream) -> TokenStream {
expand_attr_with(_attr, input, axum_test::expand)
}
/// Derive an implementation of [`axum_extra::routing::TypedPath`].
///
/// See that trait for more details.
///
/// [`axum_extra::routing::TypedPath`]: https://docs.rs/axum-extra/latest/axum_extra/routing/trait.TypedPath.html
#[proc_macro_derive(TypedPath, attributes(typed_path))]
pub fn derive_typed_path(input: TokenStream) -> TokenStream {
expand_with(input, typed_path::expand)
}
/// Derive an implementation of [`FromRef`] for each field in a struct.
///
/// # Example
///
/// ```
/// use axum::{
/// Router,
/// routing::get,
/// extract::{State, FromRef},
/// };
///
/// #
/// # type AuthToken = String;
/// # type DatabasePool = ();
/// #
/// // This will implement `FromRef` for each field in the struct.
/// #[derive(FromRef, Clone)]
/// struct AppState {
/// auth_token: AuthToken,
/// database_pool: DatabasePool,
/// // fields can also be skipped
/// #[from_ref(skip)]
/// api_token: String,
/// }
///
/// // So those types can be extracted via `State`
/// async fn handler(State(auth_token): State<AuthToken>) {}
///
/// async fn other_handler(State(database_pool): State<DatabasePool>) {}
///
/// # let auth_token = Default::default();
/// # let database_pool = Default::default();
/// let state = AppState {
/// auth_token,
/// database_pool,
/// api_token: "secret".to_owned(),
/// };
///
/// let app = Router::new()
/// .route("/", get(handler).post(other_handler))
/// .with_state(state);
/// # let _: axum::Router = app;
/// ```
///
/// [`FromRef`]: https://docs.rs/axum/0.8/axum/extract/trait.FromRef.html
#[proc_macro_derive(FromRef, attributes(from_ref))]
pub fn derive_from_ref(item: TokenStream) -> TokenStream {
expand_with(item, from_ref::expand)
}
fn expand_with<F, I, K>(input: TokenStream, f: F) -> TokenStream
where
F: FnOnce(I) -> syn::Result<K>,
I: Parse,
K: ToTokens,
{
expand(syn::parse(input).and_then(f))
}
fn expand_attr_with<F, A, I, K>(attr: TokenStream, input: TokenStream, f: F) -> TokenStream
where
F: FnOnce(A, I) -> K,
A: Parse,
I: Parse,
K: ToTokens,
{
let expand_result = (|| {
let attr = syn::parse(attr)?;
let input = syn::parse(input)?;
Ok(f(attr, input))
})();
expand(expand_result)
}
fn expand<T>(result: syn::Result<T>) -> TokenStream
where
T: ToTokens,
{
match result {
Ok(tokens) => {
let tokens = (quote! { #tokens }).into();
if std::env::var_os("AXUM_MACROS_DEBUG").is_some() {
eprintln!("{tokens}");
}
tokens
}
Err(err) => err.into_compile_error().into(),
}
}
fn infer_state_types<'a, I>(types: I) -> impl Iterator<Item = Type> + 'a
where
I: Iterator<Item = &'a Type> + 'a,
{
types
.filter_map(|ty| {
if let Type::Path(path) = ty {
Some(&path.path)
} else {
None
}
})
.filter_map(|path| {
if let Some(last_segment) = path.segments.last() {
if last_segment.ident != "State" {
return None;
}
match &last_segment.arguments {
syn::PathArguments::AngleBracketed(args) if args.args.len() == 1 => {
Some(args.args.first().unwrap())
}
_ => None,
}
} else {
None
}
})
.filter_map(|generic_arg| {
if let syn::GenericArgument::Type(ty) = generic_arg {
Some(ty)
} else {
None
}
})
.cloned()
}
#[cfg(test)]
fn run_ui_tests(directory: &str) {
#[rustversion::nightly]
fn go(directory: &str) {
let t = trybuild::TestCases::new();
if let Ok(mut path) = std::env::var("AXUM_TEST_ONLY") {
if let Some(path_without_prefix) = path.strip_prefix("axum-macros/") {
path = path_without_prefix.to_owned();
}
if !path.contains(&format!("/{directory}/")) {
return;
}
if path.contains("/fail/") {
t.compile_fail(path);
} else if path.contains("/pass/") {
t.pass(path);
} else {
panic!()
}
} else {
t.compile_fail(format!("tests/{directory}/fail/*.rs"));
t.pass(format!("tests/{directory}/pass/*.rs"));
}
}
#[rustversion::not(nightly)]
fn go(_directory: &str) {}
go(directory);
}
+439
View File
@@ -0,0 +1,439 @@
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, quote_spanned};
use syn::{parse::Parse, ItemStruct, LitStr, Token};
use crate::attr_parsing::{combine_attribute, parse_parenthesized_attribute, second, Combine};
pub(crate) fn expand(item_struct: ItemStruct) -> syn::Result<TokenStream> {
let ItemStruct {
attrs,
ident,
generics,
fields,
..
} = &item_struct;
if !generics.params.is_empty() || generics.where_clause.is_some() {
return Err(syn::Error::new_spanned(
generics,
"`#[derive(TypedPath)]` doesn't support generics",
));
}
let Attrs { path, rejection } = crate::attr_parsing::parse_attrs("typed_path", attrs)?;
let path = path.ok_or_else(|| {
syn::Error::new(
Span::call_site(),
"Missing path: `#[typed_path(\"/foo/bar\")]`",
)
})?;
let rejection = rejection.map(second);
match fields {
syn::Fields::Named(_) => {
let segments = parse_path(&path)?;
Ok(expand_named_fields(ident, path, &segments, rejection))
}
syn::Fields::Unnamed(fields) => {
let segments = parse_path(&path)?;
expand_unnamed_fields(fields, ident, path, &segments, rejection)
}
syn::Fields::Unit => expand_unit_fields(ident, path, rejection),
}
}
mod kw {
syn::custom_keyword!(rejection);
}
#[derive(Default)]
struct Attrs {
path: Option<LitStr>,
rejection: Option<(kw::rejection, syn::Path)>,
}
impl Parse for Attrs {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let mut path = None;
let mut rejection = None;
while !input.is_empty() {
let lh = input.lookahead1();
if lh.peek(LitStr) {
path = Some(input.parse()?);
} else if lh.peek(kw::rejection) {
parse_parenthesized_attribute(input, &mut rejection)?;
} else {
return Err(lh.error());
}
let _ = input.parse::<Token![,]>();
}
Ok(Self { path, rejection })
}
}
impl Combine for Attrs {
fn combine(mut self, other: Self) -> syn::Result<Self> {
let Self { path, rejection } = other;
if let Some(path) = path {
if self.path.is_some() {
return Err(syn::Error::new_spanned(
path,
"path specified more than once",
));
}
self.path = Some(path);
}
combine_attribute(&mut self.rejection, rejection)?;
Ok(self)
}
}
fn expand_named_fields(
ident: &syn::Ident,
path: LitStr,
segments: &[Segment],
rejection: Option<syn::Path>,
) -> TokenStream {
let format_str = format_str_from_path(segments);
let captures = captures_from_path(segments);
let typed_path_impl = quote_spanned! {path.span()=>
#[automatically_derived]
impl ::axum_extra::routing::TypedPath for #ident {
const PATH: &'static str = #path;
}
};
let display_impl = quote_spanned! {path.span()=>
#[automatically_derived]
impl ::std::fmt::Display for #ident {
#[allow(clippy::unnecessary_to_owned)]
#[allow(clippy::implicit_clone)]
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
let Self { #(#captures,)* } = self;
write!(
f,
#format_str,
#(
#captures = ::axum_extra::__private::utf8_percent_encode(
&#captures.to_string(),
::axum_extra::__private::PATH_SEGMENT,
)
),*
)
}
}
};
let rejection_assoc_type = rejection_assoc_type(&rejection);
let map_err_rejection = map_err_rejection(&rejection);
let from_request_impl = quote! {
#[automatically_derived]
impl<S> ::axum::extract::FromRequestParts<S> for #ident
where
S: Send + Sync,
{
type Rejection = #rejection_assoc_type;
async fn from_request_parts(
parts: &mut ::axum::http::request::Parts,
state: &S,
) -> ::std::result::Result<Self, Self::Rejection> {
<::axum::extract::Path<#ident> as ::axum::extract::FromRequestParts<S>>
::from_request_parts(parts, state)
.await
.map(|path| path.0)
#map_err_rejection
}
}
};
quote! {
#typed_path_impl
#display_impl
#from_request_impl
}
}
fn expand_unnamed_fields(
fields: &syn::FieldsUnnamed,
ident: &syn::Ident,
path: LitStr,
segments: &[Segment],
rejection: Option<syn::Path>,
) -> syn::Result<TokenStream> {
let num_captures = segments
.iter()
.filter(|segment| match segment {
Segment::Capture(_, _) => true,
Segment::Static(_) => false,
})
.count();
let num_fields = fields.unnamed.len();
if num_fields != num_captures {
return Err(syn::Error::new_spanned(
fields,
format!(
"Mismatch in number of captures and fields. Path has {} but struct has {}",
simple_pluralize(num_captures, "capture"),
simple_pluralize(num_fields, "field"),
),
));
}
let destructure_self = segments
.iter()
.filter_map(|segment| match segment {
Segment::Capture(capture, _) => Some(capture),
Segment::Static(_) => None,
})
.enumerate()
.map(|(idx, capture)| {
let idx = syn::Index {
index: idx as _,
span: Span::call_site(),
};
let capture = format_ident!("{}", capture, span = path.span());
quote_spanned! {path.span()=>
#idx: #capture,
}
});
let format_str = format_str_from_path(segments);
let captures = captures_from_path(segments);
let typed_path_impl = quote_spanned! {path.span()=>
#[automatically_derived]
impl ::axum_extra::routing::TypedPath for #ident {
const PATH: &'static str = #path;
}
};
let display_impl = quote_spanned! {path.span()=>
#[automatically_derived]
impl ::std::fmt::Display for #ident {
#[allow(clippy::unnecessary_to_owned)]
#[allow(clippy::implicit_clone)]
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
let Self { #(#destructure_self)* } = self;
write!(
f,
#format_str,
#(
#captures = ::axum_extra::__private::utf8_percent_encode(
&#captures.to_string(),
::axum_extra::__private::PATH_SEGMENT,
)
),*
)
}
}
};
let rejection_assoc_type = rejection_assoc_type(&rejection);
let map_err_rejection = map_err_rejection(&rejection);
let from_request_impl = quote! {
#[automatically_derived]
impl<S> ::axum::extract::FromRequestParts<S> for #ident
where
S: Send + Sync,
{
type Rejection = #rejection_assoc_type;
async fn from_request_parts(
parts: &mut ::axum::http::request::Parts,
state: &S,
) -> ::std::result::Result<Self, Self::Rejection> {
::axum::extract::Path::from_request_parts(parts, state)
.await
.map(|path| path.0)
#map_err_rejection
}
}
};
Ok(quote! {
#typed_path_impl
#display_impl
#from_request_impl
})
}
fn simple_pluralize(count: usize, word: &str) -> String {
if count == 1 {
format!("{count} {word}")
} else {
format!("{count} {word}s")
}
}
fn expand_unit_fields(
ident: &syn::Ident,
path: LitStr,
rejection: Option<syn::Path>,
) -> syn::Result<TokenStream> {
for segment in parse_path(&path)? {
match segment {
Segment::Capture(_, span) => {
return Err(syn::Error::new(
span,
"Typed paths for unit structs cannot contain captures",
));
}
Segment::Static(_) => {}
}
}
let typed_path_impl = quote_spanned! {path.span()=>
#[automatically_derived]
impl ::axum_extra::routing::TypedPath for #ident {
const PATH: &'static str = #path;
}
};
let display_impl = quote_spanned! {path.span()=>
#[automatically_derived]
impl ::std::fmt::Display for #ident {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, #path)
}
}
};
let rejection_assoc_type = if let Some(rejection) = &rejection {
quote! { #rejection }
} else {
quote! { ::axum::http::StatusCode }
};
let create_rejection = if let Some(rejection) = &rejection {
quote! {
Err(<#rejection as ::std::default::Default>::default())
}
} else {
quote! {
Err(::axum::http::StatusCode::NOT_FOUND)
}
};
let from_request_impl = quote! {
#[automatically_derived]
impl<S> ::axum::extract::FromRequestParts<S> for #ident
where
S: Send + Sync,
{
type Rejection = #rejection_assoc_type;
async fn from_request_parts(
parts: &mut ::axum::http::request::Parts,
_state: &S,
) -> ::std::result::Result<Self, Self::Rejection> {
if parts.uri.path() == <Self as ::axum_extra::routing::TypedPath>::PATH {
Ok(Self)
} else {
#create_rejection
}
}
}
};
Ok(quote! {
#typed_path_impl
#display_impl
#from_request_impl
})
}
fn format_str_from_path(segments: &[Segment]) -> String {
segments
.iter()
.map(|segment| match segment {
Segment::Capture(capture, _) => format!("{{{capture}}}"),
Segment::Static(segment) => segment.to_owned(),
})
.collect::<Vec<_>>()
.join("/")
}
fn captures_from_path(segments: &[Segment]) -> Vec<syn::Ident> {
segments
.iter()
.filter_map(|segment| match segment {
Segment::Capture(capture, span) => Some(format_ident!("{}", capture, span = *span)),
Segment::Static(_) => None,
})
.collect::<Vec<_>>()
}
fn parse_path(path: &LitStr) -> syn::Result<Vec<Segment>> {
let value = path.value();
if value.is_empty() {
return Err(syn::Error::new_spanned(
path,
"paths must start with a `/`. Use \"/\" for root routes",
));
} else if !path.value().starts_with('/') {
return Err(syn::Error::new_spanned(path, "paths must start with a `/`"));
}
path.value()
.split('/')
.map(|segment| {
if let Some(capture) = segment
.strip_prefix('{')
.and_then(|segment| segment.strip_suffix('}'))
.and_then(|segment| {
(!segment.starts_with('{') && !segment.ends_with('}')).then_some(segment)
})
.map(|capture| capture.strip_prefix('*').unwrap_or(capture))
{
Ok(Segment::Capture(capture.to_owned(), path.span()))
} else {
Ok(Segment::Static(segment.to_owned()))
}
})
.collect()
}
enum Segment {
Capture(String, Span),
Static(String),
}
fn path_rejection() -> TokenStream {
quote! {
<::axum::extract::Path<Self> as ::axum::extract::FromRequestParts<S>>::Rejection
}
}
fn rejection_assoc_type(rejection: &Option<syn::Path>) -> TokenStream {
match rejection {
Some(rejection) => quote! { #rejection },
None => path_rejection(),
}
}
fn map_err_rejection(rejection: &Option<syn::Path>) -> TokenStream {
rejection
.as_ref()
.map(|rejection| {
let path_rejection = path_rejection();
quote! {
.map_err(|rejection| {
<#rejection as ::std::convert::From<#path_rejection>>::from(rejection)
})
}
})
.unwrap_or_default()
}
#[test]
fn ui() {
crate::run_ui_tests("typed_path");
}
+116
View File
@@ -0,0 +1,116 @@
// this is copied from itertools under the following license
//
// Copyright (c) 2015
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the
// Software without restriction, including without
// limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice
// shall be included in all copies or substantial portions
// of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
use std::iter::{Fuse, FusedIterator, Peekable};
pub(crate) struct WithPosition<I>
where
I: Iterator,
{
handled_first: bool,
peekable: Peekable<Fuse<I>>,
}
impl<I> WithPosition<I>
where
I: Iterator,
{
pub(crate) fn new(iter: impl IntoIterator<IntoIter = I>) -> WithPosition<I> {
WithPosition {
handled_first: false,
peekable: iter.into_iter().fuse().peekable(),
}
}
}
impl<I> Clone for WithPosition<I>
where
I: Clone + Iterator,
I::Item: Clone,
{
fn clone(&self) -> Self {
Self {
handled_first: self.handled_first,
peekable: self.peekable.clone(),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub(crate) enum Position<T> {
First(T),
Middle(T),
Last(T),
Only(T),
}
impl<T> Position<T> {
pub(crate) fn into_inner(self) -> T {
match self {
Position::First(x) | Position::Middle(x) | Position::Last(x) | Position::Only(x) => x,
}
}
}
impl<I: Iterator> Iterator for WithPosition<I> {
type Item = Position<I::Item>;
fn next(&mut self) -> Option<Self::Item> {
match self.peekable.next() {
Some(item) => {
if !self.handled_first {
// Haven't seen the first item yet, and there is one to give.
self.handled_first = true;
// Peek to see if this is also the last item,
// in which case tag it as `Only`.
match self.peekable.peek() {
Some(_) => Some(Position::First(item)),
None => Some(Position::Only(item)),
}
} else {
// Have seen the first item, and there's something left.
// Peek to see if this is the last item.
match self.peekable.peek() {
Some(_) => Some(Position::Middle(item)),
None => Some(Position::Last(item)),
}
}
}
// Iterator is finished.
None => None,
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.peekable.size_hint()
}
}
impl<I> ExactSizeIterator for WithPosition<I> where I: ExactSizeIterator {}
impl<I: Iterator> FusedIterator for WithPosition<I> {}
@@ -0,0 +1,6 @@
use axum_macros::debug_handler;
#[debug_handler]
async fn handler(_foo: bool) {}
fn main() {}
@@ -0,0 +1,24 @@
error[E0277]: the trait bound `bool: FromRequest<(), axum_core::extract::private::ViaParts>` is not satisfied
--> tests/debug_handler/fail/argument_not_extractor.rs:4:24
|
4 | async fn handler(_foo: bool) {}
| ^^^^ the trait `FromRequestParts<()>` is not implemented for `bool`, which is required by `bool: FromRequest<(), _>`
|
= note: Function argument is not a valid axum extractor.
See `https://docs.rs/axum/0.8/axum/extract/index.html` for details
= help: the following other types implement trait `FromRequestParts<S>`:
`ConnectInfo<T>` implements `FromRequestParts<S>`
`Extension<T>` implements `FromRequestParts<S>`
`HeaderMap` implements `FromRequestParts<S>`
`MatchedPath` implements `FromRequestParts<S>`
`Method` implements `FromRequestParts<S>`
`OriginalUri` implements `FromRequestParts<S>`
`Query<T>` implements `FromRequestParts<S>`
`RawPathParams` implements `FromRequestParts<S>`
and $N others
= note: required for `bool` to implement `FromRequest<(), axum_core::extract::private::ViaParts>`
note: required by a bound in `__axum_macros_check_handler_0_from_request_check`
--> tests/debug_handler/fail/argument_not_extractor.rs:4:24
|
4 | async fn handler(_foo: bool) {}
| ^^^^ required by this bound in `__axum_macros_check_handler_0_from_request_check`
@@ -0,0 +1,6 @@
use axum_macros::debug_handler;
#[debug_handler(state = (), state = ())]
async fn handler() {}
fn main() {}
@@ -0,0 +1,5 @@
error: `state` specified more than once
--> tests/debug_handler/fail/duplicate_args.rs:3:29
|
3 | #[debug_handler(state = (), state = ())]
| ^^^^^
@@ -0,0 +1,9 @@
use axum::extract::Extension;
use axum_macros::debug_handler;
struct NonCloneType;
#[debug_handler]
async fn test_extension_non_clone(_: Extension<NonCloneType>) {}
fn main() {}
@@ -0,0 +1,27 @@
error[E0277]: the trait bound `NonCloneType: Clone` is not satisfied
--> tests/debug_handler/fail/extension_not_clone.rs:7:38
|
7 | async fn test_extension_non_clone(_: Extension<NonCloneType>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonCloneType`, which is required by `Extension<NonCloneType>: FromRequest<(), _>`
|
= help: the following other types implement trait `FromRequest<S, M>`:
Body
Form<T>
Json<T>
RawForm
Result<T, <T as FromRequest<S>>::Rejection>
String
axum::body::Bytes
axum::http::Request<Body>
= note: required for `Extension<NonCloneType>` to implement `FromRequestParts<()>`
= note: required for `Extension<NonCloneType>` to implement `FromRequest<(), axum_core::extract::private::ViaParts>`
note: required by a bound in `__axum_macros_check_test_extension_non_clone_0_from_request_check`
--> tests/debug_handler/fail/extension_not_clone.rs:7:38
|
7 | async fn test_extension_non_clone(_: Extension<NonCloneType>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__axum_macros_check_test_extension_non_clone_0_from_request_check`
help: consider annotating `NonCloneType` with `#[derive(Clone)]`
|
4 + #[derive(Clone)]
5 | struct NonCloneType;
|
@@ -0,0 +1,22 @@
use axum::extract::{FromRequest, Request};
use axum_macros::debug_handler;
struct A;
impl<S> FromRequest<S> for A
where
S: Send + Sync,
{
type Rejection = ();
async fn from_request(_req: Request, _state: &S) -> Result<Self, Self::Rejection> {
unimplemented!()
}
}
impl A {
#[debug_handler]
async fn handler(&mut self) {}
}
fn main() {}
@@ -0,0 +1,5 @@
error: Handlers must only take owned values
--> tests/debug_handler/fail/extract_self_mut.rs:19:22
|
19 | async fn handler(&mut self) {}
| ^^^^^^^^^
@@ -0,0 +1,22 @@
use axum::extract::{FromRequest, Request};
use axum_macros::debug_handler;
struct A;
impl<S> FromRequest<S> for A
where
S: Send + Sync,
{
type Rejection = ();
async fn from_request(_req: Request, _state: &S) -> Result<Self, Self::Rejection> {
unimplemented!()
}
}
impl A {
#[debug_handler]
async fn handler(&self) {}
}
fn main() {}
@@ -0,0 +1,5 @@
error: Handlers must only take owned values
--> tests/debug_handler/fail/extract_self_ref.rs:19:22
|
19 | async fn handler(&self) {}
| ^^^^^
@@ -0,0 +1,6 @@
use axum_macros::debug_handler;
#[debug_handler]
async fn handler<T>(_extract: T) {}
fn main() {}
@@ -0,0 +1,5 @@
error: `#[axum_macros::debug_handler]` doesn't support generic functions
--> tests/debug_handler/fail/generics.rs:4:17
|
4 | async fn handler<T>(_extract: T) {}
| ^^^
@@ -0,0 +1,6 @@
use axum_macros::debug_handler;
#[debug_handler(foo)]
async fn handler() {}
fn main() {}
@@ -0,0 +1,5 @@
error: expected `state`
--> tests/debug_handler/fail/invalid_attrs.rs:3:17
|
3 | #[debug_handler(foo)]
| ^^^
@@ -0,0 +1,9 @@
use axum::Json;
use axum_macros::debug_handler;
struct Struct {}
#[debug_handler]
async fn handler(_foo: Json<Struct>) {}
fn main() {}
@@ -0,0 +1,51 @@
error[E0277]: the trait bound `Struct: serde::Deserialize<'de>` is not satisfied
--> tests/debug_handler/fail/json_not_deserialize.rs:7:24
|
7 | async fn handler(_foo: Json<Struct>) {}
| ^^^^^^^^^^^^ the trait `for<'de> serde_core::de::Deserialize<'de>` is not implemented for `Struct`, which is required by `Json<Struct>: FromRequest<()>`
|
= note: for local types consider adding `#[derive(serde::Deserialize)]` to your `Struct` type
= note: for types from other crates check whether the crate offers a `serde` feature flag
= help: the following other types implement trait `serde_core::de::Deserialize<'de>`:
&'a [u8]
&'a serde_json::raw::RawValue
&'a std::path::Path
&'a str
()
(T,)
(T0, T1)
(T0, T1, T2)
and $N others
= note: required for `Struct` to implement `serde_core::de::DeserializeOwned`
= note: required for `Json<Struct>` to implement `FromRequest<()>`
= help: see issue #48214
help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
|
1 + #![feature(trivial_bounds)]
|
error[E0277]: the trait bound `Struct: serde::Deserialize<'de>` is not satisfied
--> tests/debug_handler/fail/json_not_deserialize.rs:7:24
|
7 | async fn handler(_foo: Json<Struct>) {}
| ^^^^^^^^^^^^ the trait `for<'de> serde_core::de::Deserialize<'de>` is not implemented for `Struct`, which is required by `Json<Struct>: FromRequest<()>`
|
= note: for local types consider adding `#[derive(serde::Deserialize)]` to your `Struct` type
= note: for types from other crates check whether the crate offers a `serde` feature flag
= help: the following other types implement trait `serde_core::de::Deserialize<'de>`:
&'a [u8]
&'a serde_json::raw::RawValue
&'a std::path::Path
&'a str
()
(T,)
(T0, T1)
(T0, T1, T2)
and $N others
= note: required for `Struct` to implement `serde_core::de::DeserializeOwned`
= note: required for `Json<Struct>` to implement `FromRequest<()>`
note: required by a bound in `__axum_macros_check_handler_0_from_request_check`
--> tests/debug_handler/fail/json_not_deserialize.rs:7:24
|
7 | async fn handler(_foo: Json<Struct>) {}
| ^^^^^^^^^^^^ required by this bound in `__axum_macros_check_handler_0_from_request_check`
@@ -0,0 +1,7 @@
use axum::extract::Path;
use axum_macros::debug_handler;
#[debug_handler]
async fn handler(_: Path<String>, _: Path<String>) {}
fn main() {}
@@ -0,0 +1,11 @@
error: Multiple parameters must be extracted with a tuple `Path<(_, _)>` or a struct `Path<YourParams>`, not by applying multiple `Path<_>` extractors
--> tests/debug_handler/fail/multiple_paths.rs:5:18
|
5 | async fn handler(_: Path<String>, _: Path<String>) {}
| ^^^^^^^^^^^^^^^
error: Multiple parameters must be extracted with a tuple `Path<(_, _)>` or a struct `Path<YourParams>`, not by applying multiple `Path<_>` extractors
--> tests/debug_handler/fail/multiple_paths.rs:5:35
|
5 | async fn handler(_: Path<String>, _: Path<String>) {}
| ^^^^^^^^^^^^^^^
@@ -0,0 +1,14 @@
use axum::{
body::Bytes,
http::{Method, Uri},
Json,
};
use axum_macros::debug_handler;
#[debug_handler]
async fn one(_: Json<()>, _: String, _: Uri) {}
#[debug_handler]
async fn two(_: Json<()>, _: Method, _: Bytes, _: Uri, _: String) {}
fn main() {}
@@ -0,0 +1,11 @@
error: Can't have two extractors that consume the request body. `Json<_>` and `String` both do that.
--> tests/debug_handler/fail/multiple_request_consumers.rs:9:14
|
9 | async fn one(_: Json<()>, _: String, _: Uri) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: Can't have more than one extractor that consume the request body. `Json<_>`, `Bytes`, and `String` all do that.
--> tests/debug_handler/fail/multiple_request_consumers.rs:12:14
|
12 | async fn two(_: Json<()>, _: Method, _: Bytes, _: Uri, _: String) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -0,0 +1,6 @@
use axum_macros::debug_handler;
#[debug_handler]
struct A;
fn main() {}
@@ -0,0 +1,5 @@
error: expected `fn`
--> tests/debug_handler/fail/not_a_function.rs:4:1
|
4 | struct A;
| ^^^^^^
@@ -0,0 +1,6 @@
use axum_macros::debug_handler;
#[debug_handler]
fn handler() {}
fn main() {}
@@ -0,0 +1,5 @@
error: Handlers must be `async fn`s
--> tests/debug_handler/fail/not_async.rs:4:1
|
4 | fn handler() {}
| ^^
@@ -0,0 +1,9 @@
use axum_macros::debug_handler;
#[debug_handler]
async fn handler() {
let _rc = std::rc::Rc::new(());
async {}.await;
}
fn main() {}
@@ -0,0 +1,20 @@
error: future cannot be sent between threads safely
--> tests/debug_handler/fail/not_send.rs:3:1
|
3 | #[debug_handler]
| ^^^^^^^^^^^^^^^^ future returned by `handler` is not `Send`
|
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`, which is required by `impl Future<Output = ()>: Send`
note: future is not `Send` as this value is used across an await
--> tests/debug_handler/fail/not_send.rs:6:14
|
5 | let _rc = std::rc::Rc::new(());
| --- has type `Rc<()>` which is not `Send`
6 | async {}.await;
| ^^^^^ await occurs here, with `_rc` maybe used later
note: required by a bound in `check`
--> tests/debug_handler/fail/not_send.rs:3:1
|
3 | #[debug_handler]
| ^^^^^^^^^^^^^^^^ required by this bound in `check`
= note: this error originates in the attribute macro `debug_handler` (in Nightly builds, run with -Z macro-backtrace for more info)
@@ -0,0 +1,28 @@
use axum::response::AppendHeaders;
#[axum::debug_handler]
async fn handler() -> (
axum::http::StatusCode,
AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
axum::http::StatusCode,
) {
panic!()
}
fn main() {}
@@ -0,0 +1,12 @@
error: Cannot return tuples with more than 17 elements
--> tests/debug_handler/fail/output_tuple_too_many.rs:4:20
|
4 | async fn handler() -> (
| ____________________^
5 | | axum::http::StatusCode,
6 | | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
7 | | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
... |
23 | | axum::http::StatusCode,
24 | | ) {
| |_^
@@ -0,0 +1,9 @@
#[axum::debug_handler]
async fn handler() -> (
axum::http::request::Parts, // this should be response parts, not request parts
axum::http::StatusCode,
) {
panic!()
}
fn main() {}
@@ -0,0 +1,8 @@
error[E0308]: mismatched types
--> tests/debug_handler/fail/returning_request_parts.rs:3:5
|
3 | axum::http::request::Parts, // this should be response parts, not request parts
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected `axum::http::response::Parts`, found `axum::http::request::Parts`
| expected `axum::http::response::Parts` because of return type
@@ -0,0 +1,10 @@
#![allow(unused_parens)]
struct NotIntoResponse;
#[axum::debug_handler]
async fn handler() -> (NotIntoResponse) {
panic!()
}
fn main() {}
@@ -0,0 +1,21 @@
error[E0277]: the trait bound `NotIntoResponse: IntoResponse` is not satisfied
--> tests/debug_handler/fail/single_wrong_return_tuple.rs:6:23
|
6 | async fn handler() -> (NotIntoResponse) {
| ^^^^^^^^^^^^^^^^^ the trait `IntoResponse` is not implemented for `NotIntoResponse`
|
= help: the following other types implement trait `IntoResponse`:
&'static [u8; N]
&'static [u8]
&'static str
()
(R,)
(Response<()>, R)
(Response<()>, T1, R)
(Response<()>, T1, T2, R)
and $N others
note: required by a bound in `__axum_macros_check_handler_into_response::{closure#0}::check`
--> tests/debug_handler/fail/single_wrong_return_tuple.rs:6:23
|
6 | async fn handler() -> (NotIntoResponse) {
| ^^^^^^^^^^^^^^^^^ required by this bound in `check`
@@ -0,0 +1,26 @@
use axum::http::Uri;
use axum_macros::debug_handler;
#[debug_handler]
async fn handler(
_e1: Uri,
_e2: Uri,
_e3: Uri,
_e4: Uri,
_e5: Uri,
_e6: Uri,
_e7: Uri,
_e8: Uri,
_e9: Uri,
_e10: Uri,
_e11: Uri,
_e12: Uri,
_e13: Uri,
_e14: Uri,
_e15: Uri,
_e16: Uri,
_e17: Uri,
) {
}
fn main() {}
@@ -0,0 +1,11 @@
error: Handlers cannot take more than 16 arguments. Use `(a, b): (ExtractorA, ExtractorA)` to further nest extractors
--> tests/debug_handler/fail/too_many_extractors.rs:6:5
|
6 | / _e1: Uri,
7 | | _e2: Uri,
8 | | _e3: Uri,
9 | | _e4: Uri,
... |
21 | | _e16: Uri,
22 | | _e17: Uri,
| |______________^
@@ -0,0 +1,10 @@
use axum::{http::Uri, Json};
use axum_macros::debug_handler;
#[debug_handler]
async fn one(_: Json<()>, _: Uri) {}
#[debug_handler]
async fn two(_: String, _: Uri) {}
fn main() {}
@@ -0,0 +1,11 @@
error: `Json<_>` consumes the request body and thus must be the last argument to the handler function
--> tests/debug_handler/fail/wrong_order.rs:5:17
|
5 | async fn one(_: Json<()>, _: Uri) {}
| ^^^^^^^^
error: `String` consumes the request body and thus must be the last argument to the handler function
--> tests/debug_handler/fail/wrong_order.rs:8:17
|
8 | async fn two(_: String, _: Uri) {}
| ^^^^^^
@@ -0,0 +1,27 @@
#![allow(unused_parens)]
#[axum::debug_handler]
async fn named_type() -> (
axum::http::StatusCode,
axum::Json<&'static str>,
axum::response::AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
) {
panic!()
}
struct CustomIntoResponse {}
impl axum::response::IntoResponse for CustomIntoResponse {
fn into_response(self) -> axum::response::Response {
todo!()
}
}
#[axum::debug_handler]
async fn custom_type() -> (
axum::http::StatusCode,
CustomIntoResponse,
axum::response::AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>,
) {
panic!()
}
fn main() {}
@@ -0,0 +1,49 @@
error: `Json<_>` must be the last element in a response tuple
--> tests/debug_handler/fail/wrong_return_tuple.rs:6:5
|
6 | axum::Json<&'static str>,
| ^^^^^^^^^^^^^^^^^^^^^^^^
error[E0277]: the trait bound `CustomIntoResponse: IntoResponseParts` is not satisfied
--> tests/debug_handler/fail/wrong_return_tuple.rs:21:5
|
21 | CustomIntoResponse,
| ^^^^^^^^^^^^^^^^^^ the trait `IntoResponseParts` is not implemented for `CustomIntoResponse`
|
= help: the following other types implement trait `IntoResponseParts`:
()
(T1, T2)
(T1, T2, T3)
(T1, T2, T3, T4)
(T1, T2, T3, T4, T5)
(T1, T2, T3, T4, T5, T6)
(T1, T2, T3, T4, T5, T6, T7)
(T1, T2, T3, T4, T5, T6, T7, T8)
and $N others
= help: see issue #48214
help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
|
3 + #![feature(trivial_bounds)]
|
error[E0277]: the trait bound `CustomIntoResponse: IntoResponseParts` is not satisfied
--> tests/debug_handler/fail/wrong_return_tuple.rs:21:5
|
21 | CustomIntoResponse,
| ^^^^^^^^^^^^^^^^^^ the trait `IntoResponseParts` is not implemented for `CustomIntoResponse`
|
= help: the following other types implement trait `IntoResponseParts`:
()
(T1, T2)
(T1, T2, T3)
(T1, T2, T3, T4)
(T1, T2, T3, T4, T5)
(T1, T2, T3, T4, T5, T6)
(T1, T2, T3, T4, T5, T6, T7)
(T1, T2, T3, T4, T5, T6, T7, T8)
and $N others
note: required by a bound in `__axum_macros_check_custom_type_into_response_parts_1_check`
--> tests/debug_handler/fail/wrong_return_tuple.rs:21:5
|
21 | CustomIntoResponse,
| ^^^^^^^^^^^^^^^^^^ required by this bound in `__axum_macros_check_custom_type_into_response_parts_1_check`
@@ -0,0 +1,8 @@
use axum_macros::debug_handler;
#[debug_handler]
async fn handler() -> bool {
false
}
fn main() {}
@@ -0,0 +1,21 @@
error[E0277]: the trait bound `bool: IntoResponse` is not satisfied
--> tests/debug_handler/fail/wrong_return_type.rs:4:23
|
4 | async fn handler() -> bool {
| ^^^^ the trait `IntoResponse` is not implemented for `bool`
|
= help: the following other types implement trait `IntoResponse`:
&'static [u8; N]
&'static [u8]
&'static str
()
(R,)
(Response<()>, R)
(Response<()>, T1, R)
(Response<()>, T1, T2, R)
and $N others
note: required by a bound in `__axum_macros_check_handler_into_response::{closure#0}::check`
--> tests/debug_handler/fail/wrong_return_type.rs:4:23
|
4 | async fn handler() -> bool {
| ^^^^ required by this bound in `check`
@@ -0,0 +1,10 @@
use axum_macros::debug_handler;
struct A;
impl A {
#[debug_handler]
async fn handler() {}
}
fn main() {}
@@ -0,0 +1,8 @@
#![deny(unreachable_code)]
use axum::extract::Path;
#[axum_macros::debug_handler]
async fn handler(Path(_): Path<String>) {}
fn main() {}
@@ -0,0 +1,9 @@
use axum_macros::debug_handler;
use std::future::Future;
#[debug_handler]
fn handler() -> impl Future<Output = ()> {
async {}
}
fn main() {}
@@ -0,0 +1,9 @@
use axum::response::IntoResponse;
use axum_macros::debug_handler;
#[debug_handler]
async fn handler() -> impl IntoResponse {
"hi!"
}
fn main() {}
@@ -0,0 +1,22 @@
use axum::extract::State;
use axum_macros::debug_handler;
#[debug_handler]
async fn handler(_: State<AppState>) {}
#[debug_handler]
async fn handler_2(_: axum::extract::State<AppState>) {}
#[debug_handler]
async fn handler_3(_: axum::extract::State<AppState>, _: axum::extract::State<AppState>) {}
#[debug_handler]
async fn handler_4(_: State<AppState>, _: State<AppState>) {}
#[debug_handler]
async fn handler_5(_: axum::extract::State<AppState>, _: State<AppState>) {}
#[derive(Clone)]
struct AppState;
fn main() {}
@@ -0,0 +1,7 @@
use axum::http::{Method, Uri};
use axum_macros::debug_handler;
#[debug_handler]
async fn handler(_one: Method, _two: Uri, _three: String) {}
fn main() {}
@@ -0,0 +1,9 @@
use axum_macros::debug_handler;
#[debug_handler]
async fn handler(mut foo: String) -> String {
foo += "bar";
foo
}
fn main() {}
@@ -0,0 +1,9 @@
use axum_macros::debug_handler;
use std::future::{ready, Ready};
#[debug_handler]
fn handler() -> Ready<()> {
ready(())
}
fn main() {}
@@ -0,0 +1,7 @@
use axum::extract::{Extension, Request};
use axum_macros::debug_handler;
#[debug_handler]
async fn handler(_: Extension<String>, _: Request) {}
fn main() {}
@@ -0,0 +1,127 @@
use axum::{extract::FromRequestParts, http::request::Parts, response::IntoResponse};
use axum_macros::debug_handler;
fn main() {}
#[debug_handler]
fn concrete_future() -> std::future::Ready<Result<impl IntoResponse, ()>> {
std::future::ready(Ok(()))
}
#[debug_handler]
fn impl_future() -> impl std::future::Future<Output = Result<impl IntoResponse, ()>> {
std::future::ready(Ok(()))
}
// === no args ===
#[debug_handler]
async fn handler_no_arg_one() -> Result<impl IntoResponse, ()> {
Ok(())
}
#[debug_handler]
async fn handler_no_arg_two() -> Result<(), impl IntoResponse> {
Err(())
}
#[debug_handler]
async fn handler_no_arg_three() -> Result<impl IntoResponse, impl IntoResponse> {
Ok::<_, ()>(())
}
#[debug_handler]
async fn handler_no_arg_four() -> Result<impl IntoResponse, impl IntoResponse> {
Err::<(), _>(())
}
// === args ===
#[debug_handler]
async fn handler_one(foo: String) -> Result<impl IntoResponse, ()> {
dbg!(foo);
Ok(())
}
#[debug_handler]
async fn handler_two(foo: String) -> Result<(), impl IntoResponse> {
dbg!(foo);
Err(())
}
#[debug_handler]
async fn handler_three(foo: String) -> Result<impl IntoResponse, impl IntoResponse> {
dbg!(foo);
Ok::<_, ()>(())
}
#[debug_handler]
async fn handler_four(foo: String) -> Result<impl IntoResponse, impl IntoResponse> {
dbg!(foo);
Err::<(), _>(())
}
// === no args with receiver ===
struct A;
impl A {
#[debug_handler]
async fn handler_no_arg_one(self) -> Result<impl IntoResponse, ()> {
Ok(())
}
#[debug_handler]
async fn handler_no_arg_two(self) -> Result<(), impl IntoResponse> {
Err(())
}
#[debug_handler]
async fn handler_no_arg_three(self) -> Result<impl IntoResponse, impl IntoResponse> {
Ok::<_, ()>(())
}
#[debug_handler]
async fn handler_no_arg_four(self) -> Result<impl IntoResponse, impl IntoResponse> {
Err::<(), _>(())
}
}
// === args with receiver ===
impl A {
#[debug_handler]
async fn handler_one(self, foo: String) -> Result<impl IntoResponse, ()> {
dbg!(foo);
Ok(())
}
#[debug_handler]
async fn handler_two(self, foo: String) -> Result<(), impl IntoResponse> {
dbg!(foo);
Err(())
}
#[debug_handler]
async fn handler_three(self, foo: String) -> Result<impl IntoResponse, impl IntoResponse> {
dbg!(foo);
Ok::<_, ()>(())
}
#[debug_handler]
async fn handler_four(self, foo: String) -> Result<impl IntoResponse, impl IntoResponse> {
dbg!(foo);
Err::<(), _>(())
}
}
impl<S> FromRequestParts<S> for A
where
S: Send + Sync,
{
type Rejection = ();
async fn from_request_parts(_parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
unimplemented!()
}
}
@@ -0,0 +1,19 @@
use axum::response::{IntoResponse, Response};
use axum_macros::debug_handler;
struct A;
impl A {
#[debug_handler]
async fn handler() -> Self {
A
}
}
impl IntoResponse for A {
fn into_response(self) -> Response {
todo!()
}
}
fn main() {}
@@ -0,0 +1,36 @@
use axum::extract::{FromRequest, Request};
use axum_macros::debug_handler;
struct A;
impl<S> FromRequest<S> for A
where
S: Send + Sync,
{
type Rejection = ();
async fn from_request(_req: Request, _state: &S) -> Result<Self, Self::Rejection> {
unimplemented!()
}
}
impl<S> FromRequest<S> for Box<A>
where
S: Send + Sync,
{
type Rejection = ();
async fn from_request(_req: Request, _state: &S) -> Result<Self, Self::Rejection> {
unimplemented!()
}
}
impl A {
#[debug_handler]
async fn handler(self) {}
#[debug_handler]
async fn handler_with_qualified_self(self: Box<Self>) {}
}
fn main() {}
@@ -0,0 +1,24 @@
use axum::extract::{FromRef, FromRequest, Request};
use axum_macros::debug_handler;
#[debug_handler(state = AppState)]
async fn handler(_: A) {}
#[derive(Clone)]
struct AppState;
struct A;
impl<S> FromRequest<S> for A
where
S: Send + Sync,
AppState: FromRef<S>,
{
type Rejection = ();
async fn from_request(_req: Request, _state: &S) -> Result<Self, Self::Rejection> {
unimplemented!()
}
}
fn main() {}
@@ -0,0 +1,10 @@
use axum::{extract::Request, extract::State};
use axum_macros::debug_handler;
#[debug_handler(state = AppState)]
async fn handler(_: State<AppState>, _: Request) {}
#[derive(Clone)]
struct AppState;
fn main() {}
@@ -0,0 +1,13 @@
use axum::{
debug_middleware,
extract::Request,
response::{IntoResponse, Response},
};
#[debug_middleware]
async fn my_middleware(request: Request) -> Response {
let _ = request;
().into_response()
}
fn main() {}
@@ -0,0 +1,7 @@
error: Middleware functions must take `axum::middleware::Next` as the last argument
--> tests/debug_middleware/fail/doesnt_take_next.rs:7:1
|
7 | #[debug_middleware]
| ^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the attribute macro `debug_middleware` (in Nightly builds, run with -Z macro-backtrace for more info)
@@ -0,0 +1,8 @@
use axum::{debug_middleware, extract::Request, middleware::Next, response::Response};
#[debug_middleware]
async fn my_middleware(next: Next, request: Request) -> Response {
next.run(request).await
}
fn main() {}
@@ -0,0 +1,5 @@
error: `axum::middleware::Next` must the last argument
--> tests/debug_middleware/fail/next_not_last.rs:4:24
|
4 | async fn my_middleware(next: Next, request: Request) -> Response {
| ^^^^^^^^^^
@@ -0,0 +1,9 @@
use axum::{debug_middleware, extract::Request, middleware::Next, response::Response};
#[debug_middleware]
async fn my_middleware(request: Request, next: Next, next2: Next) -> Response {
let _ = next2;
next.run(request).await
}
fn main() {}
@@ -0,0 +1,7 @@
error: Middleware functions can only take one argument of type `axum::middleware::Next`
--> tests/debug_middleware/fail/takes_next_twice.rs:3:1
|
3 | #[debug_middleware]
| ^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the attribute macro `debug_middleware` (in Nightly builds, run with -Z macro-backtrace for more info)
@@ -0,0 +1,8 @@
use axum::{debug_middleware, extract::Request, middleware::Next, response::Response};
#[debug_middleware]
async fn my_middleware(request: Request, next: Next) -> Response {
next.run(request).await
}
fn main() {}
@@ -0,0 +1,8 @@
use axum::extract::FromRef;
#[derive(Clone, FromRef)]
struct AppState<T> {
foo: T,
}
fn main() {}
@@ -0,0 +1,5 @@
error: `#[derive(FromRef)]` doesn't support generics
--> tests/from_ref/fail/generics.rs:4:16
|
4 | struct AppState<T> {
| ^^^
@@ -0,0 +1,22 @@
use axum::{
extract::{FromRef, State},
routing::get,
Router,
};
// This will implement `FromRef` for each field in the struct.
#[derive(Clone, FromRef)]
struct AppState {
auth_token: String,
}
// So those types can be extracted via `State`
async fn handler(_: State<String>) {}
fn main() {
let state = AppState {
auth_token: Default::default(),
};
let _: axum::Router = Router::new().route("/", get(handler)).with_state(state);
}
@@ -0,0 +1,10 @@
#![deny(noop_method_call)]
use axum_macros::FromRef;
#[derive(FromRef)]
struct State {
inner: &'static str,
}
fn main() {}
@@ -0,0 +1,10 @@
use axum_macros::FromRef;
#[derive(Clone, FromRef)]
struct AppState {
auth_token: String,
#[from_ref(skip)]
also_string: String,
}
fn main() {}
@@ -0,0 +1,9 @@
use axum_macros::FromRequest;
#[derive(FromRequest)]
struct Extractor(#[from_request(via(axum::Extension), via(axum::Extension))] State);
#[derive(Clone)]
struct State;
fn main() {}
@@ -0,0 +1,5 @@
error: `via` specified more than once
--> tests/from_request/fail/double_via_attr.rs:4:55
|
4 | struct Extractor(#[from_request(via(axum::Extension), via(axum::Extension))] State);
| ^^^
@@ -0,0 +1,12 @@
use axum_macros::FromRequest;
#[derive(FromRequest, Clone)]
#[from_request(via(axum::Extension))]
enum Extractor {
Foo {
#[from_request(via(axum::Extension))]
foo: (),
},
}
fn main() {}
@@ -0,0 +1,5 @@
error: `#[from_request(via(...))]` cannot be used inside variants
--> tests/from_request/fail/enum_from_request_ident_in_variant.rs:7:24
|
7 | #[from_request(via(axum::Extension))]
| ^^^
@@ -0,0 +1,10 @@
use axum_macros::FromRequest;
#[derive(FromRequest, Clone)]
#[from_request(via(axum::Extension))]
enum Extractor {
#[from_request(via(axum::Extension))]
Foo,
}
fn main() {}
@@ -0,0 +1,5 @@
error: `#[from_request(via(...))]` cannot be used on variants
--> tests/from_request/fail/enum_from_request_on_variant.rs:6:20
|
6 | #[from_request(via(axum::Extension))]
| ^^^
@@ -0,0 +1,6 @@
use axum_macros::FromRequest;
#[derive(FromRequest, Clone)]
enum Extractor {}
fn main() {}
@@ -0,0 +1,7 @@
error: missing `#[from_request(via(...))]`
--> tests/from_request/fail/enum_no_via.rs:3:10
|
3 | #[derive(FromRequest, Clone)]
| ^^^^^^^^^^^
|
= note: this error originates in the derive macro `FromRequest` (in Nightly builds, run with -Z macro-backtrace for more info)
@@ -0,0 +1,6 @@
use axum_macros::FromRequest;
#[derive(FromRequest)]
struct Extractor<T>(Option<T>);
fn main() {}
@@ -0,0 +1,5 @@
error: #[derive(FromRequest)] only supports generics on tuple structs that have exactly one field of the generic type
--> tests/from_request/fail/generic.rs:4:21
|
4 | struct Extractor<T>(Option<T>);
| ^^^^^^^^^
@@ -0,0 +1,11 @@
use axum::{routing::get, Router};
use axum_macros::FromRequest;
#[derive(FromRequest, Clone)]
struct Extractor<T>(T);
async fn foo(_: Extractor<()>) {}
fn main() {
_ = Router::<()>::new().route("/", get(foo));
}
@@ -0,0 +1,24 @@
error: #[derive(FromRequest)] only supports generics when used with #[from_request(via)]
--> tests/from_request/fail/generic_without_via.rs:5:18
|
5 | struct Extractor<T>(T);
| ^
error[E0277]: the trait bound `fn(Extractor<()>) -> impl Future<Output = ()> {foo}: Handler<_, _>` is not satisfied
--> tests/from_request/fail/generic_without_via.rs:10:44
|
10 | _ = Router::<()>::new().route("/", get(foo));
| --- ^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Extractor<()>) -> impl Future<Output = ()> {foo}`
| |
| required by a bound introduced by this call
|
= note: Consider using `#[axum::debug_handler]` to improve the error message
note: required by a bound in `axum::routing::get`
--> $WORKSPACE/axum/src/routing/method_routing.rs
|
| top_level_handler_fn!(get, GET);
| ^^^^^^^^^^^^^^^^^^^^^^---^^^^^^
| | |
| | required by a bound in this function
| required by this bound in `get`
= note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info)
@@ -0,0 +1,12 @@
use axum::{routing::get, Router};
use axum_macros::FromRequest;
#[derive(FromRequest, Clone)]
#[from_request(rejection(Foo))]
struct Extractor<T>(T);
async fn foo(_: Extractor<()>) {}
fn main() {
_ = Router::<()>::new().route("/", get(foo));
}
@@ -0,0 +1,24 @@
error: #[derive(FromRequest)] only supports generics when used with #[from_request(via)]
--> tests/from_request/fail/generic_without_via_rejection.rs:6:18
|
6 | struct Extractor<T>(T);
| ^
error[E0277]: the trait bound `fn(Extractor<()>) -> impl Future<Output = ()> {foo}: Handler<_, _>` is not satisfied
--> tests/from_request/fail/generic_without_via_rejection.rs:11:44
|
11 | _ = Router::<()>::new().route("/", get(foo));
| --- ^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Extractor<()>) -> impl Future<Output = ()> {foo}`
| |
| required by a bound introduced by this call
|
= note: Consider using `#[axum::debug_handler]` to improve the error message
note: required by a bound in `axum::routing::get`
--> $WORKSPACE/axum/src/routing/method_routing.rs
|
| top_level_handler_fn!(get, GET);
| ^^^^^^^^^^^^^^^^^^^^^^---^^^^^^
| | |
| | required by a bound in this function
| required by this bound in `get`
= note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info)
@@ -0,0 +1,6 @@
use axum_macros::FromRequest;
#[derive(FromRequest)]
union Extractor {}
fn main() {}

Some files were not shown because too many files have changed in this diff Show More