rust-embed v8.12.0
git clone https://git.pyrossh.dev/rust-embed
rust macro which loads files into the rust binary at compile time during release and loads the file from the fs during dev.
38f5767
— Peter John
2026-09-16T14:00:10+05:30
Add DynRustEmbed for dyn-compatible trait objects
- changelog.md +4 -0
- src/lib.rs +49 -0
- tests/lib.rs +14 -1
changelog.md
CHANGED
|
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
|
7
7
|
|
|
8
8
|
Thanks to [Mark Drobnak](https://github.com/AzureMarker) for the changelog.
|
|
9
9
|
|
|
10
|
+
## [Unreleased]
|
|
11
|
+
|
|
12
|
+
- Add `DynRustEmbed`, a dyn-compatible counterpart to `RustEmbed`, blanket-implemented for all embed types so they can be stored behind `Box<dyn DynRustEmbed>` / `&dyn DynRustEmbed`.
|
|
13
|
+
|
|
10
14
|
## [8.12.0] - 2026-07-08
|
|
11
15
|
|
|
12
16
|
- Fix embedded data being duplicated in binary. Thanks to [email protected].
|
src/lib.rs
CHANGED
|
@@ -59,6 +59,55 @@ pub trait RustEmbed {
|
|
|
59
59
|
|
|
60
60
|
pub use RustEmbed as Embed;
|
|
61
61
|
|
|
62
|
+
/// A dyn-compatible ("object safe") counterpart to [`RustEmbed`].
|
|
63
|
+
///
|
|
64
|
+
/// `RustEmbed` cannot be used as `dyn RustEmbed` because its methods have no
|
|
65
|
+
/// `self` parameter (they are associated functions on a marker type). This
|
|
66
|
+
/// trait exists for callers who need to store or pass around an embedded
|
|
67
|
+
/// directory as a trait object, e.g. `Box<dyn DynRustEmbed>`.
|
|
68
|
+
///
|
|
69
|
+
/// It is blanket-implemented for every type that implements `RustEmbed`, so
|
|
70
|
+
/// there is nothing to derive or implement manually:
|
|
71
|
+
///
|
|
72
|
+
/// ```
|
|
73
|
+
/// use rust_embed::{DynRustEmbed, Embed};
|
|
74
|
+
///
|
|
75
|
+
/// #[derive(Embed)]
|
|
76
|
+
/// #[folder = "examples/public/"]
|
|
77
|
+
/// struct Asset;
|
|
78
|
+
///
|
|
79
|
+
/// fn main() {
|
|
80
|
+
/// let embed: Box<dyn DynRustEmbed> = Box::new(Asset);
|
|
81
|
+
/// let _ = embed.get("index.html");
|
|
82
|
+
/// }
|
|
83
|
+
/// ```
|
|
84
|
+
pub trait DynRustEmbed {
|
|
85
|
+
/// Get file compressed. See [`RustEmbed::compressed`].
|
|
86
|
+
#[cfg(feature = "compression")]
|
|
87
|
+
fn compressed(&self, file_path: &str) -> Option<EmbeddedCompressedFile>;
|
|
88
|
+
|
|
89
|
+
/// Get an embedded file and its metadata. See [`RustEmbed::get`].
|
|
90
|
+
fn get(&self, file_path: &str) -> Option<EmbeddedFile>;
|
|
91
|
+
|
|
92
|
+
/// Returns the file paths in the folder. See [`RustEmbed::iter`].
|
|
93
|
+
fn names(&self) -> Vec<std::borrow::Cow<'static, str>>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
impl<T: RustEmbed> DynRustEmbed for T {
|
|
97
|
+
#[cfg(feature = "compression")]
|
|
98
|
+
fn compressed(&self, file_path: &str) -> Option<EmbeddedCompressedFile> {
|
|
99
|
+
T::compressed(file_path)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
fn get(&self, file_path: &str) -> Option<EmbeddedFile> {
|
|
103
|
+
T::get(file_path)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
fn names(&self) -> Vec<std::borrow::Cow<'static, str>> {
|
|
107
|
+
T::iter().collect()
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
62
111
|
/// An iterator over filenames.
|
|
63
112
|
///
|
|
64
113
|
/// This enum exists for optimization purposes, to avoid boxing the iterator in
|
tests/lib.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use rust_embed::{Embed, RustEmbed};
|
|
1
|
+
use rust_embed::{DynRustEmbed, Embed, RustEmbed};
|
|
2
2
|
|
|
3
3
|
/// Test doc comment
|
|
4
4
|
#[derive(Embed)]
|
|
@@ -70,3 +70,16 @@ fn iter_can_be_boxed() {
|
|
|
70
70
|
assert_eq!(filenames.len(), 7);
|
|
71
71
|
assert!(filenames.contains(&"index.html".to_string()));
|
|
72
72
|
}
|
|
73
|
+
|
|
74
|
+
/// Test that an embedded directory can be used as a trait object via
|
|
75
|
+
/// `DynRustEmbed`, since `RustEmbed` itself is not dyn-compatible.
|
|
76
|
+
#[test]
|
|
77
|
+
fn dyn_rust_embed_works() {
|
|
78
|
+
let boxed: Box<dyn DynRustEmbed> = Box::new(Asset);
|
|
79
|
+
assert!(boxed.get("index.html").is_some(), "index.html should exist");
|
|
80
|
+
assert!(boxed.get("gg.html").is_none(), "gg.html should not exist");
|
|
81
|
+
|
|
82
|
+
let names = boxed.names();
|
|
83
|
+
assert_eq!(names.len(), 7);
|
|
84
|
+
assert!(names.iter().any(|n| n == "index.html"));
|
|
85
|
+
}
|