19 Commits

Author SHA1 Message Date
347314460a Sanitize impl in macro 2024-08-05 01:49:28 +02:00
ee487540ac Sanitize returntype in macro 2024-08-05 01:46:35 +02:00
865cc6ddb9 Fix version requirement for regex 2024-07-21 19:47:15 +02:00
15de73dad4 Fix cfg for external crate 2024-07-21 19:43:58 +02:00
8cbb2757a5 Fixed version requirement 2024-07-17 13:02:09 +02:00
3389b2264e Removed trim fron inner parenthesized 2024-07-17 12:48:09 +02:00
5cd1c075a5 Read files macro for loading reading files to string at compile-time
Makefile for formatting and linting

Workspace for subcrates.

Moved crates to subdir and moved subcrate configs to workspace.*
2024-07-16 18:29:32 +02:00
7a0cf00cbc Accept IpAddr or Ipv6Addr as socket.
Added port in builder for only specifying port
2024-07-07 15:13:13 +02:00
971556af64 Merge remote-tracking branch 'origin/master' 2024-07-06 13:27:38 +02:00
f40c87aa8e Changed router visibility to pub 2024-07-06 13:27:24 +02:00
b685d81e00 comment 2024-07-03 11:24:28 +02:00
284ee73ffd Changed parsers to FnMut 2024-07-02 13:24:37 +02:00
732eaafec0 Fixed Multipartfiles only accepting a single file.
Added som standard derives on File struct, and extractors
2024-07-01 18:27:47 +02:00
963c0610e5 Removed tokio feature and added tokio to axum feature 2024-06-30 23:39:25 +02:00
0898a50166 Added MultipartFile extractors.
Moved cfg macro to lib where possible.

Changed some features, and made some deps optional
2024-06-30 20:17:44 +02:00
e0baff8625 Added state type to router macro 2024-06-28 19:28:57 +02:00
173bbc2ca5 Adde route method to builder for single route.
Removed state () from Router
2024-06-28 19:16:40 +02:00
cdc8f5e463 Moved IntoResult trait to own file outside feature 2024-06-27 11:49:41 +02:00
aba28d1612 Changed param from array slice to implInterator on routes in builder 2024-06-27 00:12:47 +02:00
38 changed files with 1763 additions and 247 deletions

View File

@ -15,4 +15,4 @@ jobs:
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose --all-features
run: cargo test --verbose --all-features --workspace

12
.idea/lib.iml generated
View File

@ -2,10 +2,18 @@
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/derive/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/derive/target" />
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/examples/multipart_file/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/crates/into_response_derive/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/crates/read_files/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/crates/read_files/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/crates/read_files/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
<excludeFolder url="file://$MODULE_DIR$/examples/multipart_file/target" />
<excludeFolder url="file://$MODULE_DIR$/crates/into_response_derive/target" />
<excludeFolder url="file://$MODULE_DIR$/crates/read_files/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />

View File

@ -1,12 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Test" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="command" value="test --package lib --lib tests --all-features" />
<configuration default="false" name="All Tests" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="command" value="test --workspace" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
<envs />
<option name="emulateTerminal" value="true" />
<option name="channel" value="DEFAULT" />
<option name="requiredFeatures" value="true" />
<option name="allFeatures" value="false" />
<option name="allFeatures" value="true" />
<option name="withSudo" value="false" />
<option name="buildTarget" value="REMOTE" />
<option name="backtrace" value="SHORT" />

19
.idea/runConfigurations/Release.xml generated Normal file
View File

@ -0,0 +1,19 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Release" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="command" value="build --release --all-features" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
<envs />
<option name="emulateTerminal" value="true" />
<option name="channel" value="DEFAULT" />
<option name="requiredFeatures" value="true" />
<option name="allFeatures" value="true" />
<option name="withSudo" value="false" />
<option name="buildTarget" value="REMOTE" />
<option name="backtrace" value="SHORT" />
<option name="isRedirectInput" value="false" />
<option name="redirectInputPath" value="" />
<method v="2">
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
</method>
</configuration>
</component>

6
.idea/rust.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RsVcsConfiguration">
<option name="rustFmt" value="true" />
</component>
</project>

228
Cargo.lock generated
View File

@ -18,10 +18,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "async-trait"
version = "0.1.80"
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "async-trait"
version = "0.1.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
dependencies = [
"proc-macro2",
"quote",
@ -47,6 +56,7 @@ dependencies = [
"matchit",
"memchr",
"mime",
"multer",
"percent-encoding",
"pin-project-lite",
"rustversion",
@ -106,15 +116,15 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bytes"
version = "1.6.0"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
[[package]]
name = "cc"
version = "1.0.99"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052"
[[package]]
name = "cfg-if"
@ -123,11 +133,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "derive"
version = "1.0.0"
name = "encoding_rs"
version = "0.8.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
dependencies = [
"quote",
"syn",
"cfg-if",
]
[[package]]
@ -203,9 +214,9 @@ dependencies = [
[[package]]
name = "http-body"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [
"bytes",
"http",
@ -238,9 +249,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "1.3.1"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d"
checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
dependencies = [
"bytes",
"futures-channel",
@ -257,9 +268,9 @@ dependencies = [
[[package]]
name = "hyper-util"
version = "0.1.5"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56"
checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956"
dependencies = [
"bytes",
"futures-util",
@ -270,6 +281,14 @@ dependencies = [
"tokio",
]
[[package]]
name = "into-response-derive"
version = "1.1.0"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "itoa"
version = "1.0.11"
@ -284,12 +303,14 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "lib"
version = "1.1.1"
version = "1.4.2-hotfix"
dependencies = [
"axum",
"derive",
"into-response-derive",
"nom",
"read-files",
"serde",
"thiserror",
"tokio",
"tokio-util",
"tower",
@ -306,9 +327,9 @@ checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "log"
version = "0.4.21"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "matchit"
@ -354,6 +375,23 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "multer"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b"
dependencies = [
"bytes",
"encoding_rs",
"futures-util",
"http",
"httparse",
"memchr",
"mime",
"spin",
"version_check",
]
[[package]]
name = "nom"
version = "7.1.3"
@ -376,9 +414,9 @@ dependencies = [
[[package]]
name = "object"
version = "0.36.0"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434"
checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce"
dependencies = [
"memchr",
]
@ -451,6 +489,44 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "read-files"
version = "0.1.0"
dependencies = [
"quote",
"regex",
"syn",
]
[[package]]
name = "regex"
version = "1.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "rustc-demangle"
version = "0.1.24"
@ -471,18 +547,18 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.203"
version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.203"
version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [
"proc-macro2",
"quote",
@ -491,9 +567,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.117"
version = "1.0.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
dependencies = [
"itoa",
"ryu",
@ -548,10 +624,16 @@ dependencies = [
]
[[package]]
name = "syn"
version = "2.0.67"
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "syn"
version = "2.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462"
dependencies = [
"proc-macro2",
"quote",
@ -570,6 +652,26 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
[[package]]
name = "thiserror"
version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.8"
@ -582,9 +684,9 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.38.0"
version = "1.38.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df"
dependencies = [
"backtrace",
"libc",
@ -734,6 +836,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@ -777,7 +885,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.5",
"windows-targets 0.52.6",
]
[[package]]
@ -797,18 +905,18 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.5",
"windows_aarch64_msvc 0.52.5",
"windows_i686_gnu 0.52.5",
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.5",
"windows_x86_64_gnu 0.52.5",
"windows_x86_64_gnullvm 0.52.5",
"windows_x86_64_msvc 0.52.5",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
@ -819,9 +927,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
@ -831,9 +939,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
@ -843,15 +951,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
@ -861,9 +969,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
@ -873,9 +981,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
@ -885,9 +993,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
@ -897,6 +1005,6 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

View File

@ -1,33 +1,54 @@
[workspace]
members = ["crates/*"]
exclude = ["examples"]
[workspace.package]
edition = "2021"
rust-version = "1.80.0"
authors = ["Martin Berg Alstad"]
homepage = "emberal.github.io"
[package]
name = "lib"
version = "1.1.1"
edition = "2021"
authors = ["Martin Berg Alstad"]
version = "1.4.2-hotfix"
description = "A library with utilities and helper fuctions."
edition = { workspace = true }
rust-version = { workspace = true }
authors = { workspace = true }
homepage = { workspace = true }
[lib]
[dependencies]
# Api
axum = { version = "0.7.5", optional = true }
tower = { version = "0.4.13", optional = true }
tower-http = { version = "0.5.2", optional = true, features = ["trace", "cors", "normalize-path"] }
axum = { version = "0.7", optional = true, features = ["multipart"] }
tower = { version = "0.4", optional = true }
tower-http = { version = "0.5", optional = true, features = ["trace", "cors", "normalize-path"] }
# Async
tokio = { version = "1.38.0", optional = true, features = ["fs"] }
tokio-util = { version = "0.7.11", optional = true, features = ["io"] }
tokio = { version = "1.38", optional = true, features = ["fs"] }
tokio-util = { version = "0.7", optional = true, features = ["io"] }
# Error handling
thiserror = { version = "1.0", optional = true }
# Logging
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
tracing = { version = "0.1", optional = true }
tracing-subscriber = { version = "0.3", optional = true }
# Parsing
nom = { version = "7.1.3", optional = true }
nom = { version = "7.1", optional = true }
# Procedural macros
into-response-derive = { path = "crates/into_response_derive", optional = true }
read-files = { path = "crates/read_files", optional = true }
# Serialization / Deserialization
serde = { version = "1.0.203", optional = true, features = ["derive"] }
# Derive macros
derive = { path = "derive", optional = true }
serde = { version = "1.0", optional = true, features = ["derive"] }
[workspace.dependencies]
syn = "2.0"
quote = "1.0"
[features]
axum = ["dep:axum", "dep:tower", "dep:tower-http"]
tokio = ["dep:tokio", "dep:tokio-util"]
vec = []
axum = ["dep:axum", "dep:tower", "dep:tower-http", "dep:thiserror", "dep:tracing", "dep:tracing-subscriber", "dep:tokio"]
io = ["dep:tokio", "dep:tokio-util"]
iter = []
nom = ["dep:nom"]
serde = ["dep:serde"]
derive = ["dep:derive", "axum", "serde"]
derive = ["dep:into-response-derive", "axum", "serde"]
read-files = ["dep:read-files"]

3
Makefile Normal file
View File

@ -0,0 +1,3 @@
fmt:
cargo clippy --all-targets --all-features
cargo fmt

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# Lib
-_-

View File

@ -0,0 +1,12 @@
[package]
name = "into-response-derive"
version = "1.1.0"
edition = { workspace = true }
rust-version = { workspace = true }
[lib]
proc-macro = true
[dependencies]
syn = { workspace = true }
quote = { workspace = true }

View File

@ -0,0 +1,20 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::DeriveInput;
pub fn into_response_derive_impl(input: DeriveInput) -> TokenStream {
let name = &input.ident;
let expanded = quote! {
impl axum::response::IntoResponse for #name {
fn into_response(self) -> axum::response::Response {
let version = env!("CARGO_PKG_VERSION");
lib::serde::response::BaseResponse::new(version, self)
.into_response()
}
}
};
TokenStream::from(expanded)
}

View File

@ -0,0 +1,13 @@
extern crate proc_macro;
use {
proc_macro::TokenStream,
syn::{parse_macro_input, DeriveInput},
};
mod derive;
#[proc_macro_derive(IntoResponse)]
pub fn into_response_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
derive::into_response_derive_impl(input)
}

View File

@ -0,0 +1,13 @@
[package]
name = "read-files"
version = "0.1.0"
edition = { workspace = true }
rust-version = { workspace = true }
[lib]
proc-macro = true
[dependencies]
syn = { workspace = true }
quote = { workspace = true }
regex = "1.10"

View File

@ -0,0 +1,34 @@
extern crate proc_macro;
use proc_macro::TokenStream;
use syn::parse_macro_input;
use crate::read_files::read_files_to_string_impl;
mod read_files;
/// Read files from a directory into a HashMap.
/// The key is the file path relative to the root directory.
/// The value is the file contents as a string.
/// # Arguments
/// * `path` - The directory to search for files, relative to the root directory.
/// * `pattern` - The regex pattern to match files against. If missing, all files are matched.
/// # Returns
/// A HashMap containing the file paths and contents.
/// # Example
/// ```
/// use read_files::read_files_to_string;
///
/// let files = read_files_to_string!("./src", ".rs$");
/// assert!(!files.is_empty());
/// ```
/// # Panics
/// If the path is empty. \
/// If the pattern is invalid. \
/// If the path does not exist. \
/// If there are unexpected tokens. \
#[proc_macro]
pub fn read_files_to_string(input: TokenStream) -> TokenStream {
let args = parse_macro_input!(input as read_files::Args);
read_files_to_string_impl(args)
}

View File

@ -0,0 +1,124 @@
extern crate proc_macro;
use proc_macro::TokenStream;
use std::{
collections::HashMap,
fs::{metadata, read_dir, read_to_string},
io,
path::{Path, PathBuf},
};
use quote::quote;
use syn::{
parse::{Parse, ParseStream},
LitStr, Token,
};
pub fn read_files_to_string_impl(args: Args) -> TokenStream {
let (keys, values) = split_hashmap(args);
let expanded = quote! {
{
let keys = vec![#( #keys, )*];
let values = vec![#( #values, )*];
keys.into_iter()
.zip(values.into_iter())
.collect::<std::collections::HashMap<&'static str, &'static str>>()
}
};
expanded.into()
}
pub struct Args {
pub path: String,
pub pattern: String,
}
struct Syntax {
path: LitStr,
/* Comma */
pattern: Option<LitStr>,
}
impl From<Syntax> for Args {
fn from(syntax: Syntax) -> Self {
Self {
path: syntax.path.value(),
pattern: syntax
.pattern
.map(|pattern| pattern.value())
.unwrap_or_default(),
}
}
}
impl Parse for Args {
fn parse(stream: ParseStream) -> syn::Result<Self> {
if stream.is_empty() {
panic!("Expected path argument");
}
let path: LitStr = stream.parse()?;
if path.value().is_empty() {
panic!("Path must not be empty");
}
let pattern = if stream.peek(Token![,]) {
stream.parse::<Token![,]>()?;
Some(stream.parse()?)
} else {
None
};
let syntax = Syntax { path, pattern };
if !stream.is_empty() {
panic!("Expected end of input");
}
Ok(syntax.into())
}
}
pub fn split_hashmap(args: Args) -> (Vec<String>, Vec<String>) {
read_files_to_string(Path::new(&args.path), &args.pattern)
.unwrap()
.into_iter()
.map(|(key, value)| (key.to_string_lossy().to_string(), value))
.collect()
}
/// Find files within a directory and load them into a HashMap.
/// The key is the file path relative to the root directory.
/// The value is the file contents as a string.
/// # Arguments
/// * `path` - The directory to search for files.
/// * `extension` - The pattern to match files against.
/// # Returns
/// A HashMap containing the file paths and contents.
pub fn read_files_to_string(
path: &Path,
pattern: &str,
) -> Result<HashMap<PathBuf, String>, io::Error> {
use regex::Regex;
let mut files: HashMap<PathBuf, String> = HashMap::new();
let dir = read_dir(path)?;
for entry in dir {
let entry = entry?;
let path = entry.path();
let file_name = entry.file_name();
let file_name = file_name.to_string_lossy();
let metadata = metadata(&path)?;
let regex =
Regex::new(pattern).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
if metadata.is_file() && regex.is_match(file_name.as_ref()) {
let file = read_to_string(&path)?;
files.insert(path, file);
} else if metadata.is_dir() {
files.extend(read_files_to_string(&path, pattern)?);
}
}
Ok(files)
}

View File

@ -0,0 +1,13 @@
use read_files::read_files_to_string;
#[test]
fn test_load_files() {
let files = read_files_to_string!("./src", ".rs$");
assert!(!files.is_empty());
}
#[test]
fn test_load_all_files() {
let files = read_files_to_string!("./src");
assert!(!files.is_empty());
}

46
derive/Cargo.lock generated
View File

@ -1,46 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "derive"
version = "1.0.0"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
version = "2.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"

View File

@ -1,12 +0,0 @@
[package]
name = "derive"
version = "1.0.0"
edition = "2021"
authors = ["Martin Berg Alstad"]
[lib]
proc-macro = true
[dependencies]
syn = "2.0.66"
quote = "1.0.36"

View File

@ -1,27 +0,0 @@
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(IntoResponse)]
pub fn into_response_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
into_response_derive_impl(input)
}
fn into_response_derive_impl(input: DeriveInput) -> TokenStream {
let name = &input.ident;
let expanded = quote! {
impl IntoResponse for #name {
fn into_response(self) -> Response {
let version = env!("CARGO_PKG_VERSION");
lib::serde::response::BaseResponse::new(version, self)
.into_response()
}
}
};
TokenStream::from(expanded)
}

941
examples/multipart_file/Cargo.lock generated Normal file
View File

@ -0,0 +1,941 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "async-trait"
version = "0.1.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "axum"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
dependencies = [
"async-trait",
"axum-core",
"bytes",
"futures-util",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-util",
"itoa",
"matchit",
"memchr",
"mime",
"multer",
"percent-encoding",
"pin-project-lite",
"rustversion",
"serde",
"serde_json",
"serde_path_to_error",
"serde_urlencoded",
"sync_wrapper 1.0.1",
"tokio",
"tower",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "axum-core"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
dependencies = [
"async-trait",
"bytes",
"futures-util",
"http",
"http-body",
"http-body-util",
"mime",
"pin-project-lite",
"rustversion",
"sync_wrapper 0.1.2",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "backtrace"
version = "0.3.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bytes"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
[[package]]
name = "cc"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "encoding_rs"
version = "0.8.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
dependencies = [
"cfg-if",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
[[package]]
name = "futures-channel"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
dependencies = [
"futures-core",
]
[[package]]
name = "futures-core"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-task"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]]
name = "futures-util"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"pin-utils",
]
[[package]]
name = "gimli"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
[[package]]
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "http"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http-body"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
dependencies = [
"bytes",
"http",
]
[[package]]
name = "http-body-util"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
dependencies = [
"bytes",
"futures-util",
"http",
"http-body",
"pin-project-lite",
]
[[package]]
name = "httparse"
version = "1.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4fe55fb7a772d59a5ff1dfbff4fe0258d19b89fec4b233e75d35d5d2316badc"
dependencies = [
"bytes",
"futures-channel",
"futures-util",
"http",
"http-body",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"smallvec",
"tokio",
]
[[package]]
name = "hyper-util"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956"
dependencies = [
"bytes",
"futures-util",
"http",
"http-body",
"hyper",
"pin-project-lite",
"tokio",
]
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "lib"
version = "1.4.2-hotfix"
dependencies = [
"axum",
"thiserror",
"tokio",
"tower",
"tower-http",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "libc"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "log"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "matchit"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "miniz_oxide"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
dependencies = [
"adler",
]
[[package]]
name = "mio"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
"wasi",
"windows-sys 0.48.0",
]
[[package]]
name = "multer"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b"
dependencies = [
"bytes",
"encoding_rs",
"futures-util",
"http",
"httparse",
"memchr",
"mime",
"spin",
"version_check",
]
[[package]]
name = "multipart_file"
version = "0.1.0"
dependencies = [
"axum",
"lib",
"tokio",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "object"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "percent-encoding"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pin-project"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pin-project-lite"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "proc-macro2"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustversion"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_path_to_error"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
dependencies = [
"itoa",
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "socket2"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "syn"
version = "2.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "sync_wrapper"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "sync_wrapper"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
[[package]]
name = "thiserror"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "tokio"
version = "1.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
dependencies = [
"backtrace",
"libc",
"mio",
"num_cpus",
"pin-project-lite",
"socket2",
"tokio-macros",
"windows-sys 0.48.0",
]
[[package]]
name = "tokio-macros"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tower"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
dependencies = [
"futures-core",
"futures-util",
"pin-project",
"pin-project-lite",
"tokio",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower-http"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
dependencies = [
"bitflags",
"bytes",
"http",
"http-body",
"http-body-util",
"pin-project-lite",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower-layer"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
[[package]]
name = "tower-service"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
[[package]]
name = "tracing"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [
"log",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
"log",
"once_cell",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
dependencies = [
"nu-ansi-term",
"sharded-slab",
"smallvec",
"thread_local",
"tracing-core",
"tracing-log",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.5",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm 0.52.5",
"windows_aarch64_msvc 0.52.5",
"windows_i686_gnu 0.52.5",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.5",
"windows_x86_64_gnu 0.52.5",
"windows_x86_64_gnullvm 0.52.5",
"windows_x86_64_msvc 0.52.5",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"

View File

@ -0,0 +1,9 @@
[package]
name = "multipart_file"
version = "0.1.0"
edition = "2021"
[dependencies]
lib = { path = "../..", features = ["axum"] }
axum = "0.7.5"
tokio = { version = "1.38.0", features = ["rt-multi-thread", "macros"] }

View File

@ -0,0 +1,37 @@
use axum::extract::DefaultBodyLimit;
use lib::axum::app::AppBuilder;
use lib::axum::extractor::MultipartFiles;
use lib::routes;
// 0 or more
async fn with_optional_file(files: Option<MultipartFiles>) -> String {
format!(
"{:?}",
files.map(|files| files
.0
.into_iter()
.map(|file| file.filename)
.collect::<Vec<_>>())
)
}
// 1 or more files
async fn handler(MultipartFiles(files): MultipartFiles) -> String {
format!(
"{:?} uploaded",
files
.into_iter()
.map(|file| file.filename)
.collect::<Vec<_>>()
)
}
#[tokio::main]
async fn main() {
let route = routes!(
get "/" => handler,
get "/opt" => with_optional_file
)
.layer(DefaultBodyLimit::disable());
AppBuilder::new().route(route).serve().await.unwrap();
}

View File

@ -1,7 +1,8 @@
#[cfg(feature = "axum")]
use std::net::IpAddr;
use {
axum::{extract::Request, handler::Handler, Router, ServiceExt},
std::net::Ipv4Addr,
std::{io, net::Ipv4Addr, net::SocketAddr},
tokio::net::TcpListener,
tower::layer::Layer,
tower_http::{
cors::CorsLayer,
@ -11,12 +12,9 @@ use {
},
tracing::{info, Level},
};
#[cfg(all(feature = "axum", feature = "tokio"))]
use {std::io, std::net::SocketAddr, tokio::net::TcpListener};
// TODO trim trailing slash into macro > let _app = NormalizePathLayer::trim_trailing_slash().layer(create_app!(routes));
#[macro_export]
#[cfg(feature = "axum")]
macro_rules! create_app {
($router:expr) => {
$router
@ -27,28 +25,41 @@ macro_rules! create_app {
}
#[derive(Default)]
#[cfg(feature = "axum")]
pub struct AppBuilder {
router: Router,
socket: Option<(Ipv4Addr, u16)>,
socket: Option<(IpAddr, u16)>,
cors: Option<CorsLayer>,
normalize_path: Option<bool>,
tracing: Option<TraceLayer<HttpMakeClassifier>>,
}
#[cfg(all(feature = "axum", feature = "tokio"))]
impl AppBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn routes(mut self, routes: &[Router]) -> Self {
self.router = routes.iter().cloned().fold(self.router, Router::merge);
pub fn route(mut self, route: Router) -> Self {
self.router = self.router.merge(route);
self
}
pub fn socket(mut self, socket: impl Into<(Ipv4Addr, u16)>) -> Self {
self.socket = Some(socket.into());
pub fn routes(mut self, routes: impl IntoIterator<Item = Router>) -> Self {
self.router = routes.into_iter().fold(self.router, Router::merge);
self
}
pub fn socket<IP: Into<IpAddr>>(mut self, socket: impl Into<(IP, u16)>) -> Self {
let (ip, port) = socket.into();
self.socket = Some((ip.into(), port));
self
}
pub fn port(mut self, port: u16) -> Self {
self.socket = if let Some((ip, _)) = self.socket {
Some((ip, port))
} else {
Some((Ipv4Addr::UNSPECIFIED.into(), port))
};
self
}
@ -77,7 +88,7 @@ impl AppBuilder {
}
pub async fn serve(self) -> io::Result<()> {
let _ = fmt_trace();
let _ = fmt_trace(); // Allowed to fail
let listener = self.listener().await?;
if self.normalize_path.unwrap_or(true) {
@ -91,7 +102,7 @@ impl AppBuilder {
}
async fn listener(&self) -> io::Result<TcpListener> {
let addr = SocketAddr::from(self.socket.unwrap_or((Ipv4Addr::UNSPECIFIED, 8000)));
let addr = SocketAddr::from(self.socket.unwrap_or((Ipv4Addr::UNSPECIFIED.into(), 8000)));
info!("Initializing server on: {addr}");
TcpListener::bind(&addr).await
}
@ -119,13 +130,12 @@ fn fmt_trace() -> Result<(), String> {
.map_err(|error| error.to_string())
}
#[cfg(all(test, feature = "axum"))]
#[cfg(test)]
mod tests {
use axum::Router;
use super::*;
#[cfg(feature = "tokio")]
mod tokio_tests {
use std::time::Duration;
@ -147,7 +157,7 @@ mod tests {
let handler = tokio::spawn(async {
AppBuilder::new()
.socket((Ipv4Addr::LOCALHOST, 8080))
.routes(&[Router::new()])
.routes([Router::new()])
.fallback(|| async { "Fallback" })
.cors(CorsLayer::new())
.normalize_path(true)

184
src/axum/extractor.rs Normal file
View File

@ -0,0 +1,184 @@
use axum::{
async_trait,
extract::{
multipart::{Field, MultipartError, MultipartRejection},
FromRequest, Multipart, Request,
},
response::IntoResponse,
};
use thiserror::Error;
#[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Debug, Clone, Copy)]
pub enum ContentType {
Json,
Form,
Multipart,
Pdf,
Html,
Unknown,
}
impl From<&str> for ContentType {
fn from(content_type: &str) -> Self {
match content_type {
"application/json" => ContentType::Json,
"application/x-www-form-urlencoded" => ContentType::Form,
"multipart/form-data" => ContentType::Multipart,
"application/pdf" => ContentType::Pdf,
"text/html" => ContentType::Html,
_ => ContentType::Unknown,
}
}
}
impl From<String> for ContentType {
fn from(content_type: String) -> Self {
ContentType::from(content_type.as_str())
}
}
impl From<Option<&str>> for ContentType {
fn from(content_type: Option<&str>) -> Self {
content_type
.map(ContentType::from)
.unwrap_or(ContentType::Unknown)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct File {
pub filename: String,
pub bytes: Vec<u8>,
pub content_type: ContentType,
}
impl File {
pub fn new(
filename: impl Into<String>,
bytes: impl Into<Vec<u8>>,
content_type: impl Into<ContentType>,
) -> Self {
Self {
filename: filename.into(),
bytes: bytes.into(),
content_type: content_type.into(),
}
}
async fn from_field(field: Field<'_>) -> Result<Self, MultipartFileRejection> {
let filename = field
.file_name()
.ok_or(MultipartFileRejection::MissingFilename)?
.to_string();
let content_type: ContentType = field.content_type().into();
let bytes = field.bytes().await?;
Ok(File::new(filename, bytes, content_type))
}
}
/// Extractor for a single file from a multipart request.
/// Expects exactly one file. A file must have a name, bytes and optionally a content type.
/// This extractor consumes the request and must ble placed last in the handler.
#[derive(Debug, Clone, PartialEq)]
pub struct MultipartFile(pub File);
/// Extractor for multiple files from a multipart request.
/// Expects at least one file. A file must have a name, bytes and optionally a content type.
/// This extractor consumes the request and must ble placed last in the handler.
#[derive(Debug, Clone, PartialEq)]
pub struct MultipartFiles(pub Vec<File>);
#[derive(Debug, Error)]
pub enum MultipartFileRejection {
#[error(transparent)]
MultipartRejection(#[from] MultipartRejection),
#[error("Field error: {0}")]
FieldError(String),
#[error("No files found")]
NoFiles,
#[error("Expected one file, got several")]
SeveralFiles,
#[error("Missing filename")]
MissingFilename,
#[error("Error in body of multipart: {0}")]
BodyError(String),
}
impl From<MultipartError> for MultipartFileRejection {
fn from(error: MultipartError) -> Self {
MultipartFileRejection::BodyError(error.body_text())
}
}
impl IntoResponse for MultipartFileRejection {
fn into_response(self) -> axum::response::Response {
match self {
MultipartFileRejection::MultipartRejection(rejection) => rejection.into_response(),
MultipartFileRejection::FieldError(error) => {
(axum::http::StatusCode::BAD_REQUEST, error).into_response()
}
MultipartFileRejection::NoFiles => {
(axum::http::StatusCode::BAD_REQUEST, "No files found").into_response()
}
MultipartFileRejection::SeveralFiles => (
axum::http::StatusCode::BAD_REQUEST,
"Expected one file, got several",
)
.into_response(),
MultipartFileRejection::MissingFilename => {
(axum::http::StatusCode::BAD_REQUEST, "Missing filename").into_response()
}
MultipartFileRejection::BodyError(error) => {
(axum::http::StatusCode::BAD_REQUEST, error).into_response()
}
}
}
}
#[async_trait]
impl<S> FromRequest<S> for MultipartFile
where
S: Send + Sync,
{
type Rejection = MultipartFileRejection;
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
let multipart = Multipart::from_request(req, state).await?;
let files = get_files(multipart).await?;
if files.len() > 1 {
Err(MultipartFileRejection::SeveralFiles)
} else {
let field = files.first().ok_or(MultipartFileRejection::NoFiles)?;
Ok(MultipartFile(field.clone()))
}
}
}
#[async_trait]
impl<S> FromRequest<S> for MultipartFiles
where
S: Send + Sync,
{
type Rejection = MultipartFileRejection;
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
let multipart = Multipart::from_request(req, state).await?;
let files = get_files(multipart).await?;
if files.is_empty() {
Err(MultipartFileRejection::NoFiles)
} else {
Ok(MultipartFiles(files))
}
}
}
async fn get_files<'a>(mut multipart: Multipart) -> Result<Vec<File>, MultipartFileRejection> {
let mut files = vec![];
while let Some(field) = multipart.next_field().await? {
files.push(File::from_field(field).await?);
}
if files.is_empty() {
Err(MultipartFileRejection::NoFiles)
} else {
Ok(files)
}
}

View File

@ -1,4 +1,4 @@
#[cfg(all(feature = "tokio", feature = "axum"))]
#[cfg(feature = "io")]
use {crate::io::file, axum::body::Body, axum::response::Html, std::io};
/// Load an HTML file from the given file path, relative to the current directory.
@ -10,7 +10,7 @@ use {crate::io::file, axum::body::Body, axum::response::Html, std::io};
/// ```
/// let html = async { lib::axum::load::load_html("openapi.html").await.unwrap() };
/// ```
#[cfg(all(feature = "tokio", feature = "axum"))]
#[cfg(feature = "io")]
pub async fn load_html<Path>(file_path: Path) -> Result<Html<Body>, io::Error>
where
Path: AsRef<std::path::Path>,
@ -18,7 +18,7 @@ where
load_file(file_path).await.map(Html)
}
#[cfg(all(feature = "tokio", feature = "axum"))]
#[cfg(feature = "io")]
pub async fn load_file<Path>(file_path: Path) -> Result<Body, io::Error>
where
Path: AsRef<std::path::Path>,
@ -38,7 +38,6 @@ where
/// ```
// TODO check platform and use correct path separator
#[macro_export]
#[cfg(feature = "axum")]
macro_rules! load_html {
($filepath:expr) => {
axum::response::Html(
@ -58,7 +57,7 @@ macro_rules! load_html {
};
}
#[cfg(all(test, feature = "axum"))]
#[cfg(test)]
mod tests {
#[test]
fn test_load_html() {
@ -71,7 +70,7 @@ mod tests {
load_html!("load.rs", "{{replace_me}}" => "hello", "{{replace_me_too}}" => "world");
}
#[cfg(feature = "tokio")]
#[cfg(feature = "io")]
mod tokio {
use super::super::*;

View File

@ -1,4 +1,6 @@
pub mod app;
pub mod extractor;
pub mod load;
#[cfg(feature = "serde")]
pub mod response;
pub mod router;

View File

@ -1,4 +1,3 @@
#[cfg(all(feature = "axum", feature = "serde"))]
use {
crate::serde::response::BaseResponse,
axum::{
@ -8,14 +7,13 @@ use {
serde::Serialize,
};
#[cfg(all(feature = "axum", feature = "serde"))]
impl<T: Serialize> IntoResponse for BaseResponse<T> {
fn into_response(self) -> Response {
Json(self).into_response()
}
}
#[cfg(all(test, feature = "axum", feature = "serde"))]
#[cfg(test)]
mod tests {
use axum::http::header::CONTENT_TYPE;
use axum::http::{HeaderValue, StatusCode};

View File

@ -18,16 +18,23 @@
/// ));
/// ```
#[macro_export]
#[cfg(feature = "axum")]
macro_rules! router {
($body:expr) => {
pub(crate) fn router() -> axum::Router<()> {
pub fn router() -> axum::Router {
$body
}
};
($body:expr; $state:ty) => {
pub fn router() -> axum::Router<$state> {
$body
}
};
($route:expr, $router:expr) => {
router!(axum::Router::new().nest($route, $router));
};
($route:expr, $router:expr, $state:ty) => {
router!(axum::Router::new().nest($route, $router); $state);
};
($($method:ident $route:expr => $func:expr),* $(,)?) => {
router!($crate::routes!($($method $route => $func),*));
};
@ -44,7 +51,6 @@ macro_rules! router {
/// );
/// ```
#[macro_export]
#[cfg(feature = "axum")]
macro_rules! routes {
($($method:ident $route:expr => $func:expr),* $(,)?) => {
axum::Router::new()
@ -55,7 +61,6 @@ macro_rules! routes {
}
#[macro_export]
#[cfg(feature = "axum")]
macro_rules! join_routes {
($($route:expr),* $(,)?) => {
axum::Router::new()$(
@ -64,8 +69,9 @@ macro_rules! join_routes {
};
}
#[cfg(all(test, feature = "axum"))]
#[cfg(test)]
mod tests {
use axum::extract::State;
use axum::Router;
async fn index() {}
@ -94,9 +100,21 @@ mod tests {
);
}
#[test]
fn test_nested_router_with_state() {
router!(
"/simplify",
routes!(
get "/:exp" => || async {},
get "/table/:exp" => |_state: State<String>| async {}
),
String
);
}
#[test]
fn test_routes() {
let _router: Router<()> = routes!(
let _router: Router = routes!(
get "/" => index,
post "/" => || async {}
);
@ -104,6 +122,6 @@ mod tests {
#[test]
fn test_join_routes() {
let _router: Router<()> = join_routes![Router::new(), Router::new()];
let _router: Router = join_routes![Router::new(), Router::new()];
}
}

View File

@ -1,7 +1,5 @@
#[cfg(feature = "tokio")]
use {std::io::Error, tokio::fs::File, tokio_util::io::ReaderStream};
#[cfg(feature = "tokio")]
pub async fn load_file<Path>(file_path: Path) -> Result<ReaderStream<File>, Error>
where
Path: AsRef<std::path::Path>,
@ -9,7 +7,7 @@ where
File::open(file_path).await.map(ReaderStream::new)
}
#[cfg(all(test, feature = "tokio"))]
#[cfg(test)]
mod tests {
use super::*;

View File

@ -1,10 +1,18 @@
#![allow(dead_code)]
pub mod axum;
pub mod io;
pub mod nom;
pub mod serde;
pub mod vector;
#[cfg(all(feature = "derive", feature = "serde"))]
pub extern crate into_response_derive;
#[cfg(feature = "read-files")]
pub extern crate read_files;
#[cfg(feature = "derive")]
pub extern crate derive;
#[cfg(feature = "axum")]
pub mod axum;
#[cfg(feature = "io")]
pub mod io;
#[cfg(feature = "nom")]
pub mod nom;
#[cfg(feature = "serde")]
pub mod serde;
pub mod traits;
#[cfg(feature = "iter")]
pub mod vector;

View File

@ -1,4 +1,3 @@
#[cfg(feature = "nom")]
use {
nom::{
bytes::complete::take_while_m_n,
@ -16,10 +15,9 @@ use {
/// - Parameters
/// - `inner`: The parser to trim
/// - Returns: A parser that trims leading and trailing whitespace from the input and then runs the value from the inner parser
#[cfg(feature = "nom")]
pub fn trim<'a, Parser, R>(inner: Parser) -> impl FnMut(&'a str) -> IResult<&'a str, R>
where
Parser: Fn(&'a str) -> IResult<&'a str, R>,
Parser: FnMut(&'a str) -> IResult<&'a str, R>,
{
delimited(multispace0, inner, multispace0)
}
@ -29,12 +27,11 @@ where
/// - Parameters
/// - `inner`: The parser to run inside the parentheses
/// - Returns: A parser that parses a parenthesized expression
#[cfg(feature = "nom")]
pub fn parenthesized<'a, Parser, R>(inner: Parser) -> impl FnMut(&'a str) -> IResult<&'a str, R>
where
Parser: Fn(&'a str) -> IResult<&'a str, R>,
Parser: FnMut(&'a str) -> IResult<&'a str, R>,
{
delimited(char('('), trim(inner), char(')'))
delimited(char('('), inner, char(')'))
}
/// Take where the predicate is true and the length is exactly `n`
@ -42,7 +39,6 @@ where
/// - `n`: The length of the string to take
/// - `predicate`: The predicate to call to validate the input
/// - Returns: A parser that takes `n` characters from the input
#[cfg(feature = "nom")]
pub fn take_where<F, Input>(n: usize, predicate: F) -> impl Fn(Input) -> IResult<Input, Input>
where
Input: InputTake + InputIter + InputLength + Slice<RangeFrom<usize>>,
@ -51,17 +47,16 @@ where
take_while_m_n(n, n, predicate)
}
#[cfg(feature = "nom")]
pub fn exhausted<'a, Parser, R>(inner: Parser) -> impl FnMut(&'a str) -> IResult<&'a str, R>
where
Parser: Fn(&'a str) -> IResult<&'a str, R>,
Parser: FnMut(&'a str) -> IResult<&'a str, R>,
{
terminated(inner, eof)
}
#[cfg(all(test, feature = "nom"))]
#[cfg(test)]
mod tests {
use nom::bytes::streaming::take_while;
use nom::{bytes::complete::take_while, sequence::tuple};
use super::*;
@ -159,4 +154,16 @@ mod tests {
let input = "test ";
assert!(exhausted(take_where(4, |c: char| c.is_ascii_alphabetic()))(input).is_err());
}
#[test]
fn test_exhausted_tuple() {
let input = "test";
let (remaining, result) = exhausted(tuple((
take_where(3, |c: char| c.is_ascii_alphabetic()),
take_while(|c: char| c.is_ascii_alphabetic()),
)))(input)
.unwrap();
assert_eq!(remaining, "");
assert_eq!(result, ("tes", "t"));
}
}

View File

@ -1,13 +1,8 @@
#[cfg(feature = "nom")]
use nom::{error::Error, IResult};
use {
crate::traits::IntoResult,
nom::{error::Error, IResult},
};
#[cfg(feature = "nom")]
pub trait IntoResult<T> {
type Error;
fn into_result(self) -> Result<T, Self::Error>;
}
#[cfg(feature = "nom")]
impl<T, R> IntoResult<T> for IResult<R, T> {
type Error = nom::Err<Error<R>>;
fn into_result(self) -> Result<T, Self::Error> {
@ -15,11 +10,12 @@ impl<T, R> IntoResult<T> for IResult<R, T> {
}
}
#[cfg(all(test, feature = "nom"))]
#[cfg(test)]
mod tests {
use super::*;
use nom::character::complete::char as c;
use super::*;
fn parse_char(input: &str) -> IResult<&str, char> {
c('A')(input)
}

View File

@ -1,15 +1,12 @@
#[cfg(feature = "serde")]
use serde::Serialize;
#[derive(Serialize)]
#[cfg(feature = "serde")]
pub struct BaseResponse<T: Serialize> {
pub version: String,
#[serde(flatten)]
pub body: T, // T must be a struct (or enum?)
}
#[cfg(feature = "serde")]
impl<T: Serialize> BaseResponse<T> {
pub fn new(version: impl Into<String>, body: T) -> Self {
Self {
@ -19,7 +16,7 @@ impl<T: Serialize> BaseResponse<T> {
}
}
#[cfg(all(test, feature = "serde"))]
#[cfg(test)]
mod tests {
use super::*;

5
src/traits.rs Normal file
View File

@ -0,0 +1,5 @@
/// Converts a type T into a Result<T, E>
pub trait IntoResult<T> {
type Error;
fn into_result(self) -> Result<T, Self::Error>;
}

View File

@ -1,9 +1,7 @@
#[cfg(feature = "vec")]
pub trait Distinct {
fn distinct(&mut self);
}
#[cfg(feature = "vec")]
impl<T: PartialEq + Clone> Distinct for Vec<T> {
fn distinct(&mut self) {
*self = self.iter().fold(vec![], |mut acc, x| {
@ -15,7 +13,7 @@ impl<T: PartialEq + Clone> Distinct for Vec<T> {
}
}
#[cfg(all(test, feature = "vec"))]
#[cfg(test)]
mod tests {
use super::*;

View File

@ -1,5 +1,4 @@
#[macro_export]
#[cfg(feature = "vec")]
macro_rules! map {
() => { std::collections::HashMap::new() };
($($k:expr => $v:expr),* $(,)?) => {
@ -13,7 +12,7 @@ macro_rules! map {
};
}
#[cfg(all(test, feature = "vec"))]
#[cfg(test)]
mod tests {
use std::collections::HashMap;

View File

@ -1,5 +1,4 @@
#[macro_export]
#[cfg(feature = "vec")]
macro_rules! matrix {
($x:expr; $m:expr, $n:expr) => {
vec![vec![$x; $n]; $m]
@ -16,7 +15,7 @@ macro_rules! matrix {
};
}
#[cfg(all(test, feature = "vec"))]
#[cfg(test)]
mod tests {
#[test]

View File

@ -1,5 +1,4 @@
#[macro_export]
#[cfg(feature = "vec")]
macro_rules! set {
() => { std::collections::HashSet::new() };
($($x:expr),* $(,)?) => {
@ -13,7 +12,7 @@ macro_rules! set {
};
}
#[cfg(all(test, feature = "vec"))]
#[cfg(test)]
mod tests {
use std::collections::HashSet;