Compare commits

...

3 Commits

Author SHA1 Message Date
a6ba8928db updated readme 2025-07-30 15:28:54 +02:00
743a6d300a removed picoserve feature
it is outside of the scope of this crate
2025-07-30 15:21:20 +02:00
659ab3f447 implemented mime mode
- required for picoserve
- moved testdata
2025-07-30 00:53:05 +02:00
20 changed files with 161 additions and 899 deletions

191
Cargo.lock generated
View File

@@ -2,141 +2,33 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "const-sha1"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8a42181e0652c2997ae4d217f25b63c5337a52fd2279736e97b832fa0a3cff"
[[package]]
name = "data-encoding"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
[[package]] [[package]]
name = "dir-embed" name = "dir-embed"
version = "0.1.1" version = "0.1.1"
dependencies = [ dependencies = [
"picoserve", "mime",
"mime_guess",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
] ]
[[package]] [[package]]
name = "embedded-io" name = "mime"
version = "0.6.1" version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]] [[package]]
name = "embedded-io-async" name = "mime_guess"
version = "0.6.1" version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
dependencies = [ dependencies = [
"embedded-io", "mime",
"unicase",
] ]
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-task"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-util"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"pin-utils",
]
[[package]]
name = "hash32"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
dependencies = [
"byteorder",
]
[[package]]
name = "heapless"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
dependencies = [
"hash32",
"serde",
"stable_deref_trait",
]
[[package]]
name = "lhash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "744a4c881f502e98c2241d2e5f50040ac73b30194d64452bb6260393b53f0dc9"
[[package]]
name = "picoserve"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c13ee221d37df77538b033b831099235067c6a3fdcd2b80ef46c3d5b96e3b2"
dependencies = [
"const-sha1",
"data-encoding",
"embedded-io-async",
"futures-util",
"heapless",
"lhash",
"picoserve_derive",
"ryu",
"serde",
"serde-json-core",
"thiserror",
]
[[package]]
name = "picoserve_derive"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82a7350bdbef1ef80e4f058b89ca974dcc6526b04ac4d2095763b69760abf7ea"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.95" version = "1.0.95"
@@ -155,49 +47,6 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde-json-core"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b81787e655bd59cecadc91f7b6b8651330b2be6c33246039a65e5cd6f4e0828"
dependencies = [
"heapless",
"ryu",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.104" version = "2.0.104"
@@ -210,24 +59,10 @@ dependencies = [
] ]
[[package]] [[package]]
name = "thiserror" name = "unicase"
version = "2.0.12" version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"

View File

@@ -13,10 +13,10 @@ proc-macro = true
[features] [features]
default = [] default = []
picoserve = ["dep:picoserve"]
[dependencies] [dependencies]
picoserve = {version = "0.16.0", optional = true } mime = { version = "0.3.17" }
mime_guess = "2.0.5"
proc-macro2 = "1" proc-macro2 = "1"
quote = "1" quote = "1"
syn = { version = "2", features = ["full"] } syn = { version = "2", features = ["full"] }

View File

@@ -1,8 +1,12 @@
Simple way to use [`include_bytes!`](https://doc.rust-lang.org/std/macro.include_bytes.html) for directories. Simple way to use [`include_bytes!`](https://doc.rust-lang.org/std/macro.include_bytes.html) for directories.
# Why
I wanted to include a directory for my microcontroller project, but none of the solutions I used before seems to work in a no_std environment.
# Example # Example
You can embed files two ways. You can embed files three ways.
## Bytes mode ## Bytes mode
@@ -39,3 +43,25 @@ fn main(){
println!("{file}"); println!("{file}");
} }
``` ```
## Mime mode
Same as "Bytes mode" but also add the guessed mime type from [`mime_guess`](https://crates.io/crates/mime_guess).
Defaults to `application/octet-stream` for unknown types.
```rust
use dir_embed::Embed;
#[derive(Embed)]
#[dir = "../web/static"] // Path is relativ to the current file
#[mode = "mime"]
pub struct Assets;
fn main(){
let file: (&[u8],&str) = Assets::get("css/style.css").expect("Can't find file");
let string = str::from_utf8(file.0).expect("Failed to parse file");
println!("{string}");
println!("MIME: {file.1}"); // text/css
}
```

View File

@@ -1 +0,0 @@
/target

View File

@@ -1,553 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
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 = "backtrace"
version = "0.3.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets",
]
[[package]]
name = "bitflags"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cfg-if"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "const-sha1"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8a42181e0652c2997ae4d217f25b63c5337a52fd2279736e97b832fa0a3cff"
[[package]]
name = "data-encoding"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
[[package]]
name = "dir-embed"
version = "0.1.1"
dependencies = [
"picoserve 0.16.0",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "embedded-io"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
[[package]]
name = "embedded-io-async"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f"
dependencies = [
"embedded-io",
]
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-task"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-util"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"pin-utils",
]
[[package]]
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "hash32"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
dependencies = [
"byteorder",
]
[[package]]
name = "heapless"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
dependencies = [
"hash32",
"serde",
"stable_deref_trait",
]
[[package]]
name = "io-uring"
version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4"
dependencies = [
"bitflags",
"cfg-if",
"libc",
]
[[package]]
name = "lhash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "744a4c881f502e98c2241d2e5f50040ac73b30194d64452bb6260393b53f0dc9"
[[package]]
name = "libc"
version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[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.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[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",
]
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
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 = "picoserve"
version = "0.1.0"
dependencies = [
"dir-embed",
"picoserve 0.16.0",
"tokio",
]
[[package]]
name = "picoserve"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c13ee221d37df77538b033b831099235067c6a3fdcd2b80ef46c3d5b96e3b2"
dependencies = [
"const-sha1",
"data-encoding",
"embedded-io-async",
"futures-util",
"heapless",
"lhash",
"picoserve_derive",
"ryu",
"serde",
"serde-json-core",
"thiserror",
"tokio",
]
[[package]]
name = "picoserve_derive"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82a7350bdbef1ef80e4f058b89ca974dcc6526b04ac4d2095763b69760abf7ea"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[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.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
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 = "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.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde-json-core"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b81787e655bd59cecadc91f7b6b8651330b2be6c33246039a65e5cd6f4e0828"
dependencies = [
"heapless",
"ryu",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
[[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",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "syn"
version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio"
version = "1.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43864ed400b6043a4757a25c7a64a8efde741aed79a056a2fb348a406701bb35"
dependencies = [
"backtrace",
"bytes",
"io-uring",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"slab",
"socket2",
"tokio-macros",
"windows-sys",
]
[[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 = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[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-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"

View File

@@ -1,15 +0,0 @@
[package]
name = "picoserve"
version = "0.1.0"
edition = "2024"
[dependencies]
dir-embed = { path = "../../", features = ["picoserve"] }
picoserve = {version = "0.16.0", features = ["tokio"] }
tokio = { version = "1", features = ["full"] }
[[bin]]
name = "picoserve"
path = "src/main.rs"

View File

@@ -1,55 +0,0 @@
use std::time::Duration;
use dir_embed::Embed;
use picoserve::routing::get;
#[derive(Embed)]
#[dir = "../static"]
struct StaticStuff;
#[tokio::main]
async fn main() {
let port = 8000;
let app = std::rc::Rc::new(
picoserve::Router::new()
.route("/api/hello", get(|| async { "Hello World" }))
.nest_service("/", StaticStuff)
.nest_service("/static", StaticStuff),
);
let config = picoserve::Config::new(picoserve::Timeouts {
start_read_request: Some(Duration::from_secs(5)),
persistent_start_read_request: Some(Duration::from_secs(1)),
read_request: Some(Duration::from_secs(1)),
write: Some(Duration::from_secs(1)),
})
.keep_connection_alive();
let socket = tokio::net::TcpListener::bind((std::net::Ipv4Addr::LOCALHOST, port))
.await
.unwrap();
tokio::task::LocalSet::new()
.run_until(async {
loop {
let (stream, remote_address) = socket.accept().await.unwrap();
println!("Connection from {remote_address}");
let app = app.clone();
let config = config.clone();
tokio::task::spawn_local(async move {
match picoserve::serve(&app, &config, &mut [0; 2048], stream).await {
Ok(handled_requests_count) => {
println!(
"{handled_requests_count} requests handled from {remote_address}"
)
}
Err(err) => println!("{err:?}"),
}
});
}
})
.await
}

View File

@@ -1,14 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Simple Static Example</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Hello from Static!</h1>
<button id="clickMeBtn">Click Me</button>
<p id="message"></p>
<script src="script.js"></script>
</body>
</html>

View File

@@ -1,3 +0,0 @@
document.getElementById("clickMeBtn").addEventListener("click", function() {
document.getElementById("message").textContent = "You clicked the button!";
});

View File

@@ -1,27 +0,0 @@
body {
font-family: Arial, sans-serif;
background: #f7f8fa;
text-align: center;
margin-top: 100px;
}
h1 {
color: #333;
}
button {
padding: 10px 25px;
font-size: 16px;
cursor: pointer;
border-radius: 5px;
border: none;
background: #4e8cff;
color: white;
margin-top: 20px;
}
#message {
margin-top: 30px;
color: #4e8cff;
font-weight: bold;
}

View File

@@ -1,3 +1,4 @@
use mime::Mime;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::quote; use quote::quote;
use syn::spanned::Spanned; use syn::spanned::Spanned;
@@ -10,6 +11,7 @@ use std::path::{Path, PathBuf};
enum EmbedMode { enum EmbedMode {
Bytes, Bytes,
Str, Str,
BytesMime,
} }
#[proc_macro_derive(Embed, attributes(dir, mode))] #[proc_macro_derive(Embed, attributes(dir, mode))]
@@ -44,6 +46,7 @@ pub fn embed(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
for entry in collect_files(&absolue_path) { for entry in collect_files(&absolue_path) {
let rel_path = entry let rel_path = entry
.0
.strip_prefix(&absolue_path) .strip_prefix(&absolue_path)
.unwrap() .unwrap()
.to_str() .to_str()
@@ -56,6 +59,11 @@ pub fn embed(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let arm = match mode { let arm = match mode {
EmbedMode::Bytes => generate_byte_arm(&rel_path, include_string), EmbedMode::Bytes => generate_byte_arm(&rel_path, include_string),
EmbedMode::Str => generate_str_arm(&rel_path, include_string), EmbedMode::Str => generate_str_arm(&rel_path, include_string),
EmbedMode::BytesMime => generate_mime_arm(
&rel_path,
include_string,
entry.1.unwrap_or(mime::APPLICATION_OCTET_STREAM),
),
}; };
match_arms.push(arm); match_arms.push(arm);
@@ -65,26 +73,26 @@ pub fn embed(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let mut expanded = match mode { let mut expanded = match mode {
EmbedMode::Bytes => generate_byte_impl(struct_name, match_arms), EmbedMode::Bytes => generate_byte_impl(struct_name, match_arms),
EmbedMode::Str => generate_str_impl(struct_name, match_arms), EmbedMode::Str => generate_str_impl(struct_name, match_arms),
EmbedMode::BytesMime => generate_mime_impl(struct_name, match_arms),
}; };
#[cfg(feature = "picoserve")]
{
expanded.extend(generate_picoserv_impl(struct_name));
}
proc_macro::TokenStream::from(expanded) proc_macro::TokenStream::from(expanded)
} }
fn collect_files(dir: &Path) -> Vec<PathBuf> { fn collect_files(dir: &Path) -> Vec<(PathBuf, Option<Mime>)> {
let mut files = Vec::new(); let mut files = Vec::new();
for entry in fs::read_dir(dir).unwrap() { for entry in fs::read_dir(dir).unwrap() {
let path = entry.unwrap().path(); let path = entry.unwrap().path();
if path.is_file() { if path.is_file() {
files.push(path); let mime_type = mime_guess::from_path(&path);
files.push((path, mime_type.first()));
} else if path.is_dir() { } else if path.is_dir() {
files.extend(collect_files(&path)); files.extend(collect_files(&path));
} }
} }
files files
} }
@@ -108,21 +116,22 @@ fn extract_dir_path(attr: &Attribute) -> String {
fn extract_mode(attr: &Attribute) -> EmbedMode { fn extract_mode(attr: &Attribute) -> EmbedMode {
let meta = match &attr.meta { let meta = match &attr.meta {
Meta::NameValue(meta) => meta, Meta::NameValue(meta) => meta,
_ => panic!("Expected #[mode = \"bytes\"|\"str\"] as a name-value attribute."), _ => panic!("Expected #[mode = \"bytes\"|\"str\"|\"mime\"] as a name-value attribute."),
}; };
let expr_lit = match &meta.value { let expr_lit = match &meta.value {
Expr::Lit(expr_lit) => expr_lit, Expr::Lit(expr_lit) => expr_lit,
_ => panic!("Expected #[mode = \"bytes\"|\"str\"] with a string literal."), _ => panic!("Expected #[mode = \"bytes\"|\"str\"|\"mime\"] with a string literal."),
}; };
match &expr_lit.lit { match &expr_lit.lit {
Lit::Str(str) => match str.value().as_str() { Lit::Str(str) => match str.value().as_str() {
"bytes" => EmbedMode::Bytes, "bytes" => EmbedMode::Bytes,
"str" => EmbedMode::Str, "str" => EmbedMode::Str,
other => panic!("Unknown mode: {other}. Use `bytes` or `str`."), "mime" => EmbedMode::BytesMime,
other => panic!("Unknown mode: {other}. Use `bytes`,`str` or `mime`."),
}, },
_ => panic!("Expected #[mode = \"bytes\"|\"str\"] to be a string."), _ => panic!("Expected #[mode = \"bytes\"|\"str\"|\"mime\"] to be a string."),
} }
} }
@@ -170,45 +179,23 @@ fn generate_str_impl(
} }
} }
#[cfg(feature = "picoserve")] fn generate_mime_arm(rel: &str, include: &str, mime_type: Mime) -> proc_macro2::TokenStream {
fn generate_picoserv_impl(struct_name: &Ident) -> proc_macro2::TokenStream { let mime_str = mime_type.essence_str();
quote! { quote! {
impl<State, CurrentPathParameters> picoserve::routing::PathRouterService<State, CurrentPathParameters> for #struct_name { #rel => Some((include_bytes!(#include),#mime_str)),
async fn call_request_handler_service<
R: picoserve::io::embedded_io_async::Read,
W: picoserve::response::ResponseWriter<Error = R::Error>,
>(
&self,
state: &State,
current_path_parameters: CurrentPathParameters,
path: picoserve::request::Path<'_>,
request: picoserve::request::Request<'_, R>,
response_writer: W,
) -> Result<picoserve::ResponseSent, W::Error> {
let requested_path = path.encoded();
let requested_file = if requested_path.is_empty() {
Self::get("index.html")
} else if let Some(striped_path) = requested_path.strip_prefix("/") {
Self::get(striped_path)
} else {
None
};
match requested_file {
Some(content) => {
let response =
picoserve::response::Response::new(picoserve::response::StatusCode::OK, content);
response_writer
.write_response(request.body_connection.finalize().await.unwrap(), response)
.await
}
None => {
use picoserve::routing::PathRouter;
picoserve::routing::NotFound.call_path_router(state,current_path_parameters,path,request,response_writer).await
} }
}
fn generate_mime_impl(
struct_name: &Ident,
match_arms: Vec<proc_macro2::TokenStream>,
) -> proc_macro2::TokenStream {
quote! {
impl #struct_name {
pub fn get(name: &str) -> Option<(&'static [u8],&'static str)> {
match name {
#(#match_arms)*
_ => None,
} }
} }
} }

1
testdata/bytes/bin vendored Normal file
View File

@@ -0,0 +1 @@
゙ュセ<EFBFBD>

12
testdata/bytes/index.html vendored Normal file
View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>
<h1>This is a Heading</h1>
<p>This is a paragraph.</p>
</body>
</html>

1
testdata/str/file1.txt vendored Normal file
View File

@@ -0,0 +1 @@
file1

1
testdata/str/sub/file2.txt vendored Normal file
View File

@@ -0,0 +1 @@
file2

View File

@@ -1,7 +1,7 @@
use dir_embed::Embed; use dir_embed::Embed;
#[derive(Embed)] #[derive(Embed)]
#[dir = "./../testdata/"] #[dir = "./../testdata/bytes/"]
#[mode = "bytes"] #[mode = "bytes"]
pub struct Assets; pub struct Assets;
@@ -41,3 +41,9 @@ fn byte_sub_directories_content() {
let content_is = Assets::get("sub/file2.txt").unwrap(); let content_is = Assets::get("sub/file2.txt").unwrap();
assert_eq!(*content_is, *content_should); assert_eq!(*content_is, *content_should);
} }
#[test]
fn byte_read_bin() {
let file = Assets::get("bin").unwrap();
assert_eq!(file, [0xDE, 0xAD, 0xBE, 0xEF]);
}

61
tests/mime.rs Normal file
View File

@@ -0,0 +1,61 @@
use dir_embed::Embed;
#[derive(Embed)]
#[dir = "./../testdata/bytes/"]
#[mode = "mime"]
pub struct Assets;
#[test]
fn mime_get() {
assert!(Assets::get("file1.txt").is_some());
}
#[test]
fn mime_get_missing() {
assert!(Assets::get("missing.txt").is_none());
}
#[test]
fn mime_read_content() {
let content_should = "file1".as_bytes();
let content_is = Assets::get("file1.txt").unwrap();
assert_eq!(*content_is.0, *content_should);
}
#[test]
fn mime_parse_string() {
let file: &[u8] = Assets::get("file1.txt").expect("Can't find file").0;
let string = str::from_utf8(file).expect("Failed to parse file");
assert_eq!(string, "file1");
}
#[test]
fn mime_sub_directories_get() {
assert!(Assets::get("sub/file2.txt").is_some());
}
#[test]
fn mime_sub_directories_content() {
let content_should = "file2".as_bytes();
let content_is = Assets::get("sub/file2.txt").unwrap();
assert_eq!(*content_is.0, *content_should);
}
#[test]
fn mime_type_html() {
let file = Assets::get("index.html").unwrap();
assert_eq!(file.1, "text/html");
}
#[test]
fn mime_type_plain() {
let file = Assets::get("file1.txt").unwrap();
assert_eq!(file.1, "text/plain");
}
#[test]
fn mime_default_type() {
let file = Assets::get("bin").unwrap();
assert_eq!(file.1, "application/octet-stream");
}

View File

@@ -1,7 +1,7 @@
use dir_embed::Embed; use dir_embed::Embed;
#[derive(Embed)] #[derive(Embed)]
#[dir = "./../testdata/"] #[dir = "./../testdata/str/"]
#[mode = "str"] #[mode = "str"]
pub struct Assets; pub struct Assets;