embed/embed.rs
1//! Load files into the Rust binary at compile time.
2//!
3//! A custom derive macro that embeds files into the binary in release builds
4//! and reads them from the file system during development. Use it to bundle
5//! CSS, JS, and images into a single executable which can be deployed to your
6//! servers, and to build small container images.
7//!
8//! The folder path given to `#[folder]` is resolved as follows:
9//!
10//! - In `release` builds, or when the `debug-embed` feature is enabled, the
11//! files are embedded into the binary and the folder is resolved relative to
12//! the directory containing `Cargo.toml` at build time.
13//! - In `debug` builds, and when the `debug-embed` feature is not enabled, the
14//! files are read from the file system at runtime. The folder captured at
15//! build time (relative to the directory containing `Cargo.toml`) is used if
16//! it still exists; otherwise the folder is resolved relative to the
17//! directory the binary is run from. The fallback keeps debug builds working
18//! when the build directory is unavailable, for example when the binary was
19//! moved to another machine or runs in a sandbox such as WASI.
20//!
21//! The folder must be a directory; deriving `Embed` on a path that does not
22//! exist or is not a directory is a compile-time error.
23//!
24//! Add the `Embed` derive to a struct with a `folder` attribute pointing at the
25//! static folder:
26//!
27//! ```
28//! use embed::Embed;
29//!
30//! #[derive(Embed)]
31//! #[folder = "examples/public/"]
32//! struct Asset;
33//! ```
34//!
35//! The macro generates two associated functions:
36//!
37//! - `get(file_path: &str) -> Option<embed::EmbeddedFile>` returns the file at
38//! a path relative to the assets folder.
39//! - `iter()` iterates over the relative paths of the files in the folder.
40//!
41//! When the files are embedded (release builds, or with `debug-embed`), the
42//! data is returned as a `Cow::Borrowed(&'static [u8])` and the file list is a
43//! static array. Otherwise the data is read from the file system on each call
44//! and the file list is produced by walking the folder.
45//!
46//! An [`EmbeddedFile`] holds the file `data` and its [`Metadata`].
47//!
48//! # Lookup paths
49//!
50//! `get` accepts the same relative paths that `iter` yields: `/`-separated
51//! paths with no empty, `.` or `..` component and no backslash, for example
52//! `"index.html"` or `"images/llama.png"`. Any other input returns `None`
53//! instead of being normalized, so `get("./index.html")`,
54//! `get("images/../index.html")` and absolute paths always return `None`. This
55//! guarantees that debug builds behave exactly like release builds: in both
56//! cases the only accepted inputs are the exact paths listed by `iter`.
57//!
58//! Paths are relative to the folder and never escape it, including through
59//! symbolic links: a symlink whose target is outside the folder is ignored by
60//! `iter` and returns `None` from `get`, in debug and release builds alike.
61//! Symlinks that stay inside the folder are followed normally.
62//!
63//! Files whose names are not valid UTF-8, or whose names contain a backslash,
64//! are skipped when the folder is walked (they cannot be addressed by a `&str`
65//! path). This matches the behaviour of Go's `embed` package.
66//!
67//! Two limits are worth knowing about:
68//!
69//! - On case-insensitive file systems (for example the default macOS
70//! configuration), a debug lookup may resolve a path whose casing differs
71//! from the stored name, while embedded lookups are always case sensitive.
72//! Pass names exactly as returned by `iter`.
73//! - Debug lookups check the canonical path and then read it. A local attacker
74//! able to modify the assets folder while the program runs could swap a path
75//! between the check and the read; making this race-free requires
76//! handle-relative APIs that are not available in safe `std`. Release builds
77//! are unaffected, since the data is embedded.
78//!
79//! # Attributes
80//!
81//! ## `prefix`
82//!
83//! `#[prefix = "my_prefix/"]` prepends a prefix to every file path. The prefix
84//! is required on `get` calls and is included in the paths returned by `iter`.
85//!
86//! ## `metadata_only`
87//!
88//! `#[metadata_only = true]` excludes the file contents from the binary. Only
89//! file paths and metadata are embedded.
90//!
91//! ## `crate_path`
92//!
93//! `#[crate_path = "some::path::Embed"]` names the crate that provides the
94//! `EmbeddedFile`, `Metadata` and `RustEmbed` items used by the generated code.
95//! It defaults to `embed` and is meant for re-exporting scenarios.
96//!
97//! # Features
98//!
99//! - `debug-embed`: always embed the files in the binary, even in debug builds.
100//! - `guess-media-type`: add `Metadata::mimetype()` and guess each file's
101//! media type from its path.
102//!
103//! # Reproducible builds
104//!
105//! When the `SOURCE_DATE_EPOCH` environment variable is set, its value (in
106//! seconds since the UNIX epoch) is used for both
107//! [`Metadata::last_modified`] and [`Metadata::created`], so builds of the same
108//! sources produce the same metadata. It is read at most once per process.
109//!
110//! # Example
111//!
112//! ```
113//! use embed::Embed;
114//!
115//! #[derive(Embed)]
116//! #[folder = "examples/public/"]
117//! #[prefix = "prefix/"]
118//! struct Asset;
119//!
120//! fn main() {
121//! let index_html = Asset::get("prefix/index.html").unwrap();
122//! println!("{:?}", std::str::from_utf8(index_html.data.as_ref()));
123//!
124//! for file in Asset::iter() {
125//! println!("{}", file.as_ref());
126//! }
127//! }
128//! ```
129#![forbid(unsafe_code)]
130pub use embed_utils::{EmbeddedFile, Metadata};
131#[cfg(feature = "compression")]
132#[cfg_attr(feature = "compression", doc(hidden))]
133pub use include_flate::flate;
134
135#[doc(hidden)]
136pub extern crate embed_utils as utils;
137
138/// A directory of binary assets.
139///
140/// The files in the specified folder will be embedded into the executable in
141/// release builds. Debug builds will read the data from the file system at
142/// runtime.
143///
144/// This trait is meant to be derived like so:
145/// ```
146/// use embed::Embed;
147///
148/// #[derive(Embed)]
149/// #[folder = "examples/public/"]
150/// struct Asset;
151///
152/// fn main() {}
153/// ```
154pub trait RustEmbed {
155 /// Get an embedded file and its metadata.
156 ///
157 /// If the feature `debug-embed` is enabled or the binary was compiled in
158 /// release mode, the file information is embedded in the binary and the file
159 /// data is returned as a `Cow::Borrowed(&'static [u8])`.
160 ///
161 /// Otherwise, the information is read from the file system on each call and
162 /// the file data is returned as a `Cow::Owned(Vec<u8>)`.
163 ///
164 /// Returns `None` for paths that are not valid relative paths (see the
165 /// crate documentation) and for files that do not exist.
166 fn get(file_path: &str) -> Option<EmbeddedFile>;
167
168 /// Iterates over the file paths in the folder.
169 ///
170 /// If the feature `debug-embed` is enabled or the binary is compiled in
171 /// release mode, a static array containing the list of relative file paths
172 /// is used.
173 ///
174 /// Otherwise, the files are listed from the file system on each call.
175 fn iter() -> Filenames;
176}
177
178pub use embed_impl::RustEmbed as Embed;
179
180/// An iterator over filenames.
181///
182/// This enum exists for optimization purposes, to avoid boxing the iterator in
183/// some cases. Do not try and match on it: which variant is constructed depends
184/// on the compilation context.
185///
186/// Both variants are always defined, even though only one of them is ever
187/// constructed for a given build. Gating them on `debug_assertions` would make
188/// the shape of this enum depend on the profile of the crate that compiles it,
189/// which breaks down when the crate and its users are not built with the same
190/// profile (for example rustdoc, which builds doctests with debug assertions
191/// enabled while linking the release library).
192pub enum Filenames {
193 /// Release builds use a named iterator type, which can be stack-allocated.
194 Embedded(std::slice::Iter<'static, &'static str>),
195
196 /// The debug iterator type is currently unnameable and still needs to be
197 /// boxed.
198 Dynamic(Box<dyn Iterator<Item = std::borrow::Cow<'static, str>>>),
199}
200
201impl Iterator for Filenames {
202 type Item = std::borrow::Cow<'static, str>;
203 fn next(&mut self) -> Option<Self::Item> {
204 match self {
205 Filenames::Embedded(names) => names.next().map(|x| std::borrow::Cow::from(*x)),
206 Filenames::Dynamic(boxed) => boxed.next(),
207 }
208 }
209}