embed_it
Include any directory as a struct, and the entire tree will be generated as Rust structures and traits
Imagine a project structure like this:
- assets/
- one_txt/
- hello
- world
- hello.txt
- one.txt
- world.txt
- one_txt/
- src
- Cargo.toml
You can use a macro to expand it into Rust code:
use Embed;
;
#
Known issues
Long compilation time with many files
If your directory contains a very large number of files, the compile time can increase significantly.
Possible solution: Move those assets into a separate crate. This way, the main build won’t be slowed down by the large amount of embedded content, and changes in the asset crate won’t force a full rebuild of your main project.
macro invocation exceeds token limit
error in rust-analyzer
When there are thousands of files/directories (around 5000 or more), rust-analyzer can fail with the error that the macro exceeds the token limit. This is due to a hard-coded limit in rust-analyzer that is not currently configurable tracking issue.
Possible workaround: Split the assets into multiple directories and generate several smaller embedded structures, each containing fewer files, to reduce the total token count.
Intellisense issues in RustRover
In JetBrains RustRover, intellisense might stop working when the number of files/directories reaches a similar high threshold. The exact cause and any permanent solution are currently unclear.
Possible workaround: As above, splitting assets into multiple directories with separate macro invocations may help avoid hitting internal limits.
Fields
embed
The main attribute
field | type | multiple | required | default | description |
---|---|---|---|---|---|
path |
String |
false | true | - | The path to the directory with assets. It may contain compile-time environment variables (or user defined) in format $CARGO_MANIFEST_DIR or ${CARGO_MANIFEST_DIR} |
dir |
DirAttr |
false | false | DirAttr::default() |
Changes the setting for how the Dir trait and its implementations are generated. See more in the Dir Attr section |
file |
FileAttr |
false | false | FileAttr::default() |
Changes the setting for how the File trait and its implementations are generated. See more in the File Attr section |
entry |
EntryAttr |
false | false | EntryAttr::default() |
Changes the setting for how the Entry struct and its implementations are generated. See more in the Entry Attr section |
with_extension |
bool |
false | false | false |
Use file extensions for method and struct names |
support_alt_separator |
bool |
false | false | false |
If true, getting a value from the directory's Index replaces \ with / . In other words, you can use Windows-style paths with the get method, for example, Assets.get("a\\b\\c.txt") |
DirAttr
field | type | multiple | required | default | description |
---|---|---|---|---|---|
derive_default_traits |
bool |
false | false | true |
Determines whether default traits will be derived (see the derive row in the table) |
trait_name |
Ident |
false | false | Dir |
Specifies the trait name that will be used for a directory |
field_factory_trait_name |
Ident |
false | false | DirFieldFactory |
Specifies the trait name that will be used for a directory field factory |
derive |
Vec<DirTrait> |
true | false | Path , Entries , Index , Meta , Debug DirectChildCount , RecursiveChildCount |
What traits will be derived for every directory and what bounds will be set for the Dir trait. See also EmbeddedTraits list and Hash traits |
field |
Vec<FieldAttr> |
true | false | vec![] |
Adds additional fields for a directory. See more in the Field Attr section |
FileAttr
field | type | multiple | required | default | description |
---|---|---|---|---|---|
derive_default_traits |
bool |
false | false | true |
Determines whether default traits will be derived (see the derive row in the table) |
trait_name |
Ident |
false | false | File |
What trait name will be used for a directory |
field_factory_trait_name |
Ident |
false | false | FileFieldFactory |
What trait name will be used for a directory field factory |
derive |
Vec<DirTrait> |
true | false | Path , Meta , Debug , Content |
What traits will be derived for every directory and what bounds will be set for a Dir trait. See also EmbeddedTraits list and Hash traits |
field |
Vec<FieldAttr> |
true | false | vec![] |
Adds additional fields for a file. See more in the Field Attr section |
EmbeddedTraits list
name | trait | dir or file | method | purpose |
---|---|---|---|---|
Path | [crate::EntryPath ] |
any | fn path(&self) -> &'static EmbeddedPath; |
Provides full information about a path of an entry |
Entries | <auto generated> | dir | fn entries(&self) -> &'static [Entry] |
Provides direct children of a dir |
Index | <auto generated> | dir | fn get(&self, path: &str) -> Option<&'static Entry> |
Provides fast access (HashMap ) to all children (recursively). It constructs hash set on every level dir and might use some memory if there are a lot of entries |
DirectChildCount | [crate::DirectChildCount ] |
dir | fn direct_child_count(&self) -> usize; |
Provides the number of direct children |
RecursiveChildCount | [crate::RecursiveChildCount ] |
dir | fn recursive_child_count(&self) -> usize; |
Provides the total number of children, including nested subdirectories |
Meta | [crate::Meta ] |
any | fn metadata(&self) -> &'static Metadata; |
Provides metadata of an entry |
Debug | [std::fmt::Debug ] |
any | Debugs structs | |
Content | [crate::Content ] |
file | fn content(&self) -> &'static [u8]; |
Provides content of a file |
Hashes | <various> | any | fn <name>[<_bits>](&self) -> &'static [u8; <bits>]; |
Provides hash of a file content or a directory structure with files' hashes |
EntryAttr
field | type | multiple | required | default | description |
---|---|---|---|---|---|
struct_name |
Ident |
false | false | Entry |
What struct name will be used for an entry |
FieldAttr
You can add any additional fields, which will be created in runtime (but only once) from a dir or a file.
For each field
defined in macros a special trait will be generated inside the module containing a root structure.
field | type | multiple | required | default | description |
---|---|---|---|---|---|
name |
Ident |
false | true | The name of the method that will be used by the trait | |
factory |
syn::Path |
false | true | The path to a factory, that will be used to create an instance of the field and to determine a field type | |
trait_name |
Option<Ident> |
false | false | {name.to_pascal_case()}Field |
The name of the field trait |
regex |
Option<String> |
false | false | None |
Regular expression to match a fs entry path. The trait is implemented for a struct only if the regex matches |
pattern |
Option<String> |
false | false | None |
Glob pattern to match a fs entry path. The trait is implemented for a struct only if the pattern matches |
use from_utf8;
use Embed;
;
;
;
#
Hash traits
You can use any combination of hash traits on dir
and file
. For a file, it hashes its content; for a directory, it hashes every entry name and entry hash if applicable (order — directories first, then files, and finally by path). The hash is stored as a constant array of bytes.
Derive | Required feature | Trait |
---|---|---|
Md5 |
md5 |
[crate::Md5Hash ] |
Sha1 |
sha1 |
[crate::Sha1Hash ] |
Sha2_224 |
sha2 |
[crate::Sha2_224Hash ] |
Sha2_256 |
sha2 |
[crate::Sha2_256Hash ] |
Sha2_384 |
sha2 |
[crate::Sha2_384Hash ] |
Sha2_512 |
sha2 |
[crate::Sha2_512Hash ] |
Sha3_224 |
sha3 |
[crate::Sha3_224Hash ] |
Sha3_256 |
sha3 |
[crate::Sha3_256Hash ] |
Sha3_384 |
sha3 |
[crate::Sha3_384Hash ] |
Sha3_512 |
sha3 |
[crate::Sha3_512Hash ] |
Blake3 |
blake3 |
[crate::Blake3_256Hash ] |
The example below compiles only if all hash features listed in the table above are enabled.
More complex example
use Embed;
;
;
// The name of the factory as in the attribute `dir`
;
// The name of the factory as in the attribute `file`
#
How does fs-entry's name turn into Rust identifiers?
Each name will be processed and any unsuitable symbol will be replaced with _
. This might cause a problem with a level uniqueness of identifiers, for example, all of the entry names below turn into one_txt
.
- one+txt
- one-txt
- one_txt
The macro handles this problem and generates methods with a numeric suffix. In that case it would be
- one+txt -
one_txt()
- one-txt -
one_txt_1()
- one_txt -
one_txt_2()
Entries are sorted unambiguously by entry kind (directories first, then files) and subsequently by path.
This works for struct names in the same way
- one+txt -
OneTxt
- one-txt -
OneTxt1
- one_txt -
OneTxt2
What code will be generated by macros
- The macro generates all embedded trait definitions, like
Entries
andIndex
which depend on a context - The macro generates definitions for traits
Dir
andFile
where each is a compilation of previous step suitable traits - The macro generates enum for the
Entry
(Dir(&'static dyn Dir)
/File(&'static dyn File)
). - The macro implements the intersection of the
Dir
andFile
traits for theEntry
struct - The macro generates traits for
FileFieldFactory
andDirFieldFactory
with bounds toFile
/Dir
traits for the argument of the method - The macro generates traits for each
field
- For any entry starting from the root:
- For each type of entry, the macro implements the requested suitable embedded traits (like
Content
,Path
,Metadata
,Entries
,Index
, etc.) - For each type of entry, the macro implements traits for all suitable fields from the step 6
- For a directory, the macro recursively generates code for each child
- For each type of entry, the macro implements the requested suitable embedded traits (like
NOTE: All instances are static, and this staticness is achieved
- by
const
for any const items, like file content or file path
;
- by a
static
LazyLock
for non-const items, which can be created without a context
;
- by a
static
OnceLock
for non-const items, which require a context (like additionalfield
s)
// user-defined struct and implementation
;
;
// auto-generated
;
;