fs-embed 0.2.1

Embed files in release, read from disk in debug — with a unified API.
Documentation

fs-embed

crates.io docs.rs License: MIT OR Apache-2.0

Embed directories into your binary at compile time, or read from disk at runtime, with a unified and ergonomic API. Supports overlays, dynamic mode, and directory composition.


Features

  • Embed directories or files at compile time using a single macro: fs_embed!()
  • Switch between embedded and disk-backed (dynamic) mode at runtime
  • Compose multiple directories with overlays using DirSet
  • Access files and subdirectories by path, list entries, and walk recursively
  • Overlay/override support: later directories take precedence
  • No build scripts, no config, no env vars
  • Robust error handling and path normalization

Example Usage

use fs_embed::fs_embed;

// Embed the "static" folder
static STATIC: fs_embed::Dir = fs_embed!("static");

fn main() {
    // Use disk in debug, embedded in release
    let dir = STATIC.auto_dynamic();
    if let Some(file) = dir.get_file("css/style.css") {
        let content = file.read_str().unwrap();
        println!("{content}");
    }
}

Macro Usage and Options

The fs_embed! macro expects a literal relative path inside your crate, resolved via CARGO_MANIFEST_DIR.

static DIR: fs_embed::Dir = fs_embed!("assets");

By default, in debug mode, files are read from disk for hot-reload; in release mode, files are embedded in the binary.

Example with options:

static DIR: fs_embed::Dir = fs_embed!("assets");

Directory API

Dir

  • Dir::get_file(path) — Get a file by relative path
  • Dir::get_dir(path) — Get a subdirectory by relative path
  • Dir::entries() — List all immediate entries (files and subdirectories)
  • Dir::walk() — Recursively yield all files
  • Dir::is_embedded() — Returns true if directory is embedded
  • Dir::into_dynamic() — Always use disk (dynamic) mode
  • Dir::auto_dynamic() — Use disk in debug, embedded in release

File

  • File::file_name() — Get the file name
  • File::extension() — Get the file extension
  • File::read_bytes() — Read file contents as bytes
  • File::read_str() — Read file contents as UTF-8 string
  • File::metadata() — Get file metadata (size, modified time)

DirSet (Overlays)

You can compose multiple directories using DirSet to support overlays and override semantics. Later directories in the set override files from earlier ones with the same relative path.

use fs_embed::{fs_embed, DirSet};

static BASE: fs_embed::Dir = fs_embed!("base");
static THEME: fs_embed::Dir = fs_embed!("theme");

let set = DirSet::new(vec![BASE, THEME]);
if let Some(file) = set.get_file("index.html") {
    // File from THEME if it exists, otherwise BASE
}
  • Overlay precedence is left-to-right
  • Only the highest-precedence file for each path is returned by walk_override()

Tests

This crate includes comprehensive tests for all public API. To run tests:

cargo test -p fs-embed

License

This crate is dual-licensed under either the MIT or Apache-2.0 license, at your option. See LICENSE-MIT and LICENSE-APACHE for details.



Related Crate: rust-silos

If you only need to embed files and access them by path (without directory traversal, overlays, or virtual filesystem features), consider using rust-silos. It provides a minimal, highly efficient, and allocation-free API for file embedding, ideal for simple use cases where directory APIs are not required.


Links