Rust Macro which loads files into the rust binary at compile time during release and loads the file from the fs during dev.

Overview

Rust Embed Build Status crates.io

Rust Custom Derive Macro which loads files into the rust binary at compile time during release and loads the file from the fs during dev.

You can use this to embed your css, js and images into a single executable which can be deployed to your servers. Also it makes it easy to build a very small docker image for you to deploy.

Dev

Release

Installation

[dependencies]
rust-embed="5.9.0"

Documentation

You need to add the custom derive macro RustEmbed to your struct with an attribute folder which is the path to your static folder.

The path resolution works as follows:

  • In debug and when debug-embed feature is not enabled, the folder path is resolved relative to where the binary is run from.
  • In release or when debug-embed feature is enabled, the folder path is resolved relative to where Cargo.toml is.
#[derive(RustEmbed)]
#[folder = "examples/public/"]
struct Asset;

The macro will generate the following code:

impl Asset {
  pub fn get(file_path: &str) -> Option<Cow<'static, [u8]>> {
    ...
  }

  pub fn iter() -> impl Iterator<Item = Cow<'static, str>> {
    ...
  }
}
impl RustEmbed for Asset {
  fn get(file_path: &str) -> Option<Cow<'static, [u8]>> {
    ...
  }
  fn iter() -> impl Iterator<Item = Cow<'static, str>> {
    ...
  }
}

get(file_path: &str)

Given a relative path from the assets folder returns the bytes if found.

If the feature debug-embed is enabled or the binary compiled in release mode the bytes have been embeded in the binary and a Cow::Borrowed(&'static [u8]) is returned.

Otherwise the bytes are read from the file system on each call and a Cow::Owned(Vec<u8>) is returned.

iter()

Iterates the files in this assets folder.

If the feature debug-embed is enabled or the binary compiled in release mode a static array to the list of relative paths to the files is returned.

Otherwise the files are listed from the file system on each call.

The prefix attribute

You can add #[prefix = "my_prefix/"] to the RustEmbed struct to add a prefix to all of the file paths. This prefix will be required on get calls, and will be included in the file paths returned by iter.

Features

debug-embed

Always embed the files in the binary, even in debug mode.

interpolate-folder-path

Allow environment variables to be used in the folder path. Example:

#[derive(RustEmbed)]
#[folder = "$CARGO_MANIFEST_DIR/foo"]
struct Asset;

This will pull the foo directory relative to your Cargo.toml file.

compression

Compress each file when embedding into the binary. Compression is done via include-flate.

Usage

use rust_embed::RustEmbed;

#[derive(RustEmbed)]
#[folder = "examples/public/"]
#[prefix = "prefix/"]
struct Asset;

fn main() {
  let index_html = Asset::get("prefix/index.html").unwrap();
  println!("{:?}", std::str::from_utf8(index_html.as_ref()));

  for file in Asset::iter() {
      println!("{}", file.as_ref());
  }
}

Examples

To run the example in dev mode where it reads from the fs,

cargo run --example basic

To run the example in release mode where it reads from binary,

cargo run --example basic --release

Note: To run the actix-web example:

cargo run --example actix --features actix

Note: To run the rocket example, add the nightly feature flag and run on a nightly build:

cargo +nightly run --example rocket --features nightly

Note: To run the warp example:

cargo run --example warp --features warp-ex

Testing

debug: cargo test --test lib

release: cargo test --test lib --release

Go Rusketeers! The power is yours!

Comments
  • Avoid allocating new Vec for every get() call

    Avoid allocating new Vec for every get() call

    This allocates a new Vec for every get() call: https://github.com/pyros2097/rust-embed/blob/b509d63985c4079c0cd2aaf2e3c951ee249003d5/src/lib.rs#L62

    It would be better to return a &'static [u8] by writing this instead:

        #key => Some(&include_bytes!(#canonical_path_str)[..]),
    

    And then pub fn get(file_path: &str) -> Option<&'static [u8]> {

    enhancement help wanted 
    opened by Boscop 17
  • Feature Request: Include some file metadata w/ the file

    Feature Request: Include some file metadata w/ the file

    I'm using rust-embed to create an executable that serves some files over HTTP.

    HTTP has support for metadata, like the Last-Modified date, and ETag, which can help browser caches.

    If rust-embed included the last-modified date, as well as some cryptographic hash (say, sha256 and/or sha512?), then I could use that data to serve Last-Modified and ETag headers to help browsers more effectively cache these resources.

    In the meantime, I suppose I could work around the issue by calculating content hashes in memory. It'd be handy to have them at compile time, though.

    opened by NfNitLoop 14
  • RustEmbed trait

    RustEmbed trait

    My stab at implementing #48; in addition to the existing impl{} block for the struct, the codegen also implements a new RustEmbed trait.

    The RustEmbed trait provides get() and iter() methods, working pretty much the same way as (and mostly delegating to) their inherent counterparts.

    The implementation is object-safe, so you could (for example) stick multiple asset folders into an array.

    opened by Tangent128 11
  • sonatype-2021-4646

    sonatype-2021-4646

    cargo pants reports the following vulnerability:

    Vulnerable Dependencies

    [1/1] pkg:cargo/[email protected] 1 known vulnerability found

    Vulnerability Title: 1 vulnerability found ╭─────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ ID │ sonatype-2021-4646 │ ├─────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │ Description │ 1 non-CVE vulnerability found. To see more details, please create a free account at https://ossindex.sonatype.org/ and request │ │ │ for this information using your registered account │ ├─────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │ CVSS Score │ 7.8 │ ├─────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │ CVSS Vector │ CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H │ ├─────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │ Reference │ https://ossindex.sonatype.org │ ╰─────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

    Inverse Dependency graph rust-embed-impl 6.2.0 (git+https://github.com/pyrossh/rust-embed#451568cd0c93a9fd363829f54c17785e432bf4e8) └── rust-embed 6.4.0 (git+https://github.com/pyrossh/rust-embed#451568cd0c93a9fd363829f54c17785e432bf4e8)

    opened by acim 9
  • "GET /img/menu_logo.76408655.png HTTP/1.1" 404

    "GET /img/menu_logo.76408655.png HTTP/1.1" 404 [2022-05-12T10:29:03Z INFO xdls::controller::statics] img-params===Path("menu_logo.76408655.png") [2022-05-12T10:29:03Z INFO xdls::controller::statics] handle_embedded_file path====img/menu_logo.76408655.png

    "GET /fonts/element-icons.535877f5.woff HTTP/1.1" 404 [2022-05-12T10:30:10Z INFO xdls::controller::statics] fonts-params===Path("element-icons.535877f5.woff") [2022-05-12T10:30:10Z INFO xdls::controller::statics] handle_embedded_file path====fonts/element-icons.535877f5.woff

    "GET /css/menu_logo.76408655.png HTTP/1.1" 200 [2022-05-12T10:27:20Z INFO xdls::controller::statics] handle_embedded_file path====css/menu_logo.76408655.png

    "GET /img/logs.fa1e7ddd.css HTTP/1.1" 200 [2022-05-12T10:25:39Z INFO xdls::controller::statics] img-params===Path("logs.fa1e7ddd.css") [2022-05-12T10:25:39Z INFO xdls::controller::statics] handle_embedded_file path====img/logs.fa1e7ddd.css

    Currently all files in the Fonts folder will be 404. Currently all files in the IMG folder except logs.fa1e7ddd.css are 404.

    actix-web = "4.0.0" rust-embed = "6.4.0"

    MacBookPro:static qxq$ tree . ├── css │   ├── app.2748f048.css │   ├── chunk-vendors.4ba11380.css │   ├── chunk-vendors.4ba11380.css.gz │   ├── config.39fdc8fc.css │   ├── config~home.7ae7c0ef.css │   ├── home.ad65ad08.css │   ├── login.6d5cd834.css │   ├── logs.fa1e7ddd.css │   ├── menu_logo.76408655.png │   └── system.9e578feb.css ├── favicon.ico ├── fonts │   ├── element-icons.535877f5.woff │   └── element-icons.732389de.ttf ├── img │   ├── detection1.0aea7e16.jpg │   ├── detection2.b13e79df.jpg │   ├── icon_car.b91f3b70.png │   ├── icon_radar.e1631f11.png │   ├── img_1.8229e084.png │   ├── logo.734d0fe1.png │   ├── logs.fa1e7ddd.css │   └── menu_logo.76408655.png ├── index.html └── js ├── app.e012053f.js ├── app.e012053f.js.gz ├── chunk-vendors.17c8ec50.js ├── chunk-vendors.17c8ec50.js.gz ├── config.7094d2aa.js ├── config.7094d2aa.js.gz ├── config~home.fe5be4d4.js ├── config~home.fe5be4d4.js.gz ├── config~home~login~logs~system.5fa4ace5.js ├── config~home~login~logs~system.5fa4ace5.js.gz ├── home.17d84104.js ├── home.17d84104.js.gz ├── login.f2d94fbe.js ├── logs.d11034de.js ├── system.98ea62e3.js └── system.98ea62e3.js.gz

    4 directories, 38 files

    #[derive(RustEmbed)]
    #[folder = "static/"]
    struct Asset;
    
    
    fn handle_embedded_file(path: &str) -> HttpResponse {
        log::info!("handle_embedded_file path===={}", path);
        match Asset::get(path) {
            Some(content) => {
                // let body: Vec<u8> = match content.data {
                //     Cow::Borrowed(bytes) => {
                //         bytes.into()
                //     },
                //     Cow::Owned(bytes) => {
                //         bytes.into()
                //     }
                // };
                let body = content.data.into_owned();
    
                HttpResponse::Ok().content_type(from_path(path).first_or_octet_stream().as_ref()).body(body)
            }
            None => HttpResponse::NotFound().body("404 Not Found"),
        }
    }
    
    pub async fn index() -> HttpResponse {
        handle_embedded_file("index.html")
    }
    
    pub async fn css(params: web::Path<String>) -> HttpResponse {
        let path = format!("css/{}", params.into_inner());
        handle_embedded_file(&path)
    }
    
    pub async fn js(params: web::Path<String>) -> HttpResponse {
        let path = format!("js/{}", params.into_inner());
        handle_embedded_file(&path)
    }
    
    pub async fn fonts(params: web::Path<String>) -> HttpResponse {
        log::info!("fonts-params==={:?}", params);
        let path = format!("fonts/{}", params.into_inner());
        handle_embedded_file(&path)
    }
    
    pub async fn img(params: web::Path<String>) -> HttpResponse {
        log::info!("img-params==={:?}", params);
        let path = format!("img/{}", params.into_inner());
        handle_embedded_file(&path)
    }
    
    pub async fn ico() -> HttpResponse {
        handle_embedded_file("favicon.ico")
    }
    
    
    .service(
                web::resource("/")
                    .route(web::get().to(statics::index)),
            )
            .route("/css/{path}", web::get().to(statics::css))
            .route("/js/{path}", web::get().to(statics::js))
            .route("/fonts/{path}", web::get().to(statics::fonts))
            .route("/img/{path}", web::get().to(statics::img))
            .route("/favicon.ico", web::get().to(statics::ico))
    
    opened by qxqxq 9
  • empty file list when asset directory is in nested folder

    empty file list when asset directory is in nested folder

    Hi,

    Given:

    \ (the root of cargo workspace)
    \Cargo.toml
    \assets\ (a simple folder)
    \component-a\ (a crate within the workspace)
    \component-a\Cargo.toml
    \component-a\assets
    \static\ (a simple folder)
    \static\assets
    

    where each "\assets" directory is a copy with at least a.txt in, then #[folder = "assets/"] is the only one that works when used within \component-a.

    Observations:

    • #[folder = "component-a/assets"] compiles but lists no files
    • #[folder = "static/assets"] compiles but lists no files

    this is regardless of whether cargo run is run within \ or \component-a.

    What I am trying to achieve is either component-a/assets/ or static/assets/.

    Help :-)

    opened by yatesco 9
  • appveyor windows compile

    appveyor windows compile

    I noticed something when trying to use the git repo directly in Cargo.toml.

    rust-embed = { git = "https://github.com/pyros2097/rust-embed" }
    

    Compiling with Appveyor, the build errored out with this message:

    Compiling rust-embed-impl v4.4.0 (https://github.com/pyros2097/rust-embed#de8e97fe)
    error: expected item, found `..`
     --> C:\Users\appveyor\.cargo\git\checkouts\rust-embed-88640eaf411660a2\de8e97f\impl\src\utils.rs:1:1
      |
    1 | ../../src/utils.rs
      | ^^ expected item
    error: aborting due to previous error
    error: Could not compile `rust-embed-impl`.
    

    The latest released version (v4.4.0) was building successfully.

    opened by gambhiro 9
  • Compile error with 'debug-embed'

    Compile error with 'debug-embed'

    I have a base library which embeds some icons using rust-embed. When using the library from another project the embedded resources can only be found when running the binary in release mode. Trying to run the application in debug mode where the resources are read from the file system yields run-time errors. So I would like to turn on the debug-embed feature to always embed the resource like in the release build. However by doing this

    rust-embed = { version = "4.2", features = ["debug-embed"] }
    

    I get the following compile errors

      |
    9 | #[derive(RustEmbed)]
      |          ^^^^^^^^^ could not find `utils` in `rust_embed`
    

    Any help would be greatly appreciated.

    opened by jangernert 9
  • Rocket example doesn't compile: can't find crate for `rocket_codegen`

    Rocket example doesn't compile: can't find crate for `rocket_codegen`

    D:\3rdparty\rust-embed [master ≡]> cargo run --example rocket
    ←[0m←[0m←[1m←[32m    Updating←[0m registry `https://github.com/rust-lang/crates.io-index`
    ←[0m←[0m←[1m←[32m   Compiling←[0m winapi v0.3.5
    ←[0m←[0m←[1m←[32m   Compiling←[0m unicode-xid v0.0.4
    ←[0m←[0m←[1m←[32m   Compiling←[0m quote v0.3.15
    ←[0m←[0m←[1m←[32m   Compiling←[0m synom v0.11.3
    ←[0m←[0m←[1m←[32m   Compiling←[0m syn v0.11.11
    ←[0m←[0m←[1m←[32m   Compiling←[0m same-file v1.0.2
    ←[0m←[0m←[1m←[32m   Compiling←[0m walkdir v2.1.4
    ←[0m←[0m←[1m←[32m   Compiling←[0m rust-embed v3.0.0 (file:///D:/3rdparty/rust-embed)
    ←[0m←[1m←[38;5;9merror[E0463]←[0m←[0m←[1m←[38;5;15m: can't find crate for `rocket_codegen`←[0m
    ←[0m ←[0m←[0m←[1m←[38;5;14m--> ←[0m←[0mexamples\rocket.rs:2:11←[0m
    ←[0m  ←[0m←[0m←[1m←[38;5;14m|←[0m
    ←[0m←[1m←[38;5;14m2←[0m←[0m ←[0m←[0m←[1m←[38;5;14m| ←[0m←[0m#![plugin(rocket_codegen)]←[0m
    ←[0m  ←[0m←[0m←[1m←[38;5;14m| ←[0m←[0m          ←[0m←[0m←[1m←[38;5;9m^^^^^^^^^^^^^^←[0m←[0m ←[0m←[0m←[1m←[38;5;9mcan't f
    ind crate←[0m
    
    ←[0m←[1m←[38;5;9merror←[0m←[0m←[1m←[38;5;15m: aborting due to previous error←[0m
    
    ←[0m←[1m←[38;5;15mFor more information about this error, try `rustc --explain E0463`.←[0m
    ←[0m←[0m←[1m←[31merror:←[0m Could not compile `rust-embed`.
    
    To learn more, run the command again with --verbose.
    
    opened by Boscop 9
  • Saving Rocket static and template files

    Saving Rocket static and template files

    I'd a look at rocket example but my issue is different, I've the below code:

    #![feature(decl_macro, proc_macro_hygiene)]
    use rocket_contrib::templates::Template;
    use rocket_contrib::serve::StaticFiles;
    mod routes;
    
    use rust_embed::RustEmbed;
    
    #[derive(RustEmbed)]
    #[folder = "static"]
    struct Static;
    
    #[derive(RustEmbed)]
    #[folder = "templates"]
    struct Templates;
    
    fn main() {
            rocket::ignite()
            .attach(Template::fairing())
            .mount("/static",
                    StaticFiles::from("static"))
            .mount("/", rocket::routes![routes::root::root])
            .launch();
    }
    

    And the app structure is as shown: image

    And my Cargo.toml is:

    [dependencies]
    rust-embed = {version = "5.5.1", features = ["interpolate-folder-path"]}
    rocket = "0.4.4"
    [dependencies.rocket_contrib]
    version = "0.4.4"
    defaullt-features = "false"
    features = ["tera_templates", "serve"]
    

    Once I got the .exe file generated at Win 10 it did not work without copying the static and templates folder in the same directory of the executable, am I missing something to get them embed in the binary itself?

    opened by hyousefGopher 8
  • Create a Changelog

    Create a Changelog

    I haven't found any place which documents the changes in each version, so I assume the changes have never been documented besides the commit messages. It would be helpful to create a changelog file or use GitHub Releases to document the changes in each version.

    opened by AzureMarker 8
  • Allow dynamically reading either from the file system or the embedded binary, with configurable runtime order

    Allow dynamically reading either from the file system or the embedded binary, with configurable runtime order

    Hi,

    Thanks for the great crate!

    I'd like to request a new feature where files are always embedded regardless of build type, but queried files can be read either from the file system or from the embedded blob, configurable at run time, with fallback to the other access kind.

    For example I'd configure filesystem access to have precedence. In that case:

    1. if a.txt exists in the FS, it would be returned
    2. if a.txt does not exist in FS, the implementation would try to look it up in the embedded blob and return it from there
    3. If a.txt does not exist either in the FS, nor is it embedded, then None would be returned.

    This is similar to what golang's go.rice package offers. https://github.com/GeertJohan/go.rice

    It allows configuring the order of precedence. https://pkg.go.dev/github.com/GeertJohan/go.rice#Config

    I realize this is likely a bigger design change compared to what the crate currently offers, but i think it would be a useful addition. If adding fallbacks is too much work, a simpler change would a new feature to always generate both the fs and embedded code paths, and introducing a new api that would take as a param which code path to use at runtime. Then application code can handle fallbacks and the query order.

    opened by alcroito 1
  • Store the result from `mime_guess` statically at compile time?

    Store the result from `mime_guess` statically at compile time?

    The examples all show using mime_guess at runtime while serving the file to guess the mimetype, but it would be nice if there were a mime_guess feature that would make it statically compile the result of mime_guess into the EmbeddedFile type.

    opened by BBaoVanC 3
Owner
Peter
@EqualExperts(@iowna, @zeta, @lifebox-healthcare), previous @unbarrier, @playlyfe
Peter
The [cain!] macro is a macro that rewrites sequential Rust branch statements into nested branches

Note! This crate is experimental and under development. It may include bugs that alter the behavior of your code in unexpected ways. You should review

Fredrik Østrem 2 Jan 19, 2022
Rust Macros to automate the addition of Paths/Schemas to Utoipa crate, simulating Reflection during the compilation phase

utoipa_auto_discovery Rust Macros to automate the addition of Paths/Schemas to Utoipa crate, simulating Reflection during the compilation phase Crate

null 4 Jun 9, 2023
lispr is a Rust macro that tries to implement a small subset of LISPs syntax in Rust

lispr lispr is a Rust macro that tries to implement a small subset of LISPs syntax in Rust. It is neither especially beautiful or efficient since it i

Jan Vaorin 0 Feb 4, 2022
Attribute macro that generates negated versions of`is_something` functions

negate negate is a simple attribute macro that negates a given function. Usage #[negate] Given a function of the form is_* that returns a boolean valu

Vinícius Miguel 9 Mar 4, 2022
A fast static site generator in a single binary with everything built-in. https://www.getzola.org

zola (né Gutenberg) A fast static site generator in a single binary with everything built-in. Documentation is available on its site or in the docs/co

Zola 10.1k Jan 10, 2023
A Rust application which funnels external webhook event data to an Urbit chat.

Urbit Webhook Funnel This is a simple Rust application which funnels external webhook event data to an Urbit chat. This application is intended to be

Robert Kornacki 15 Jan 2, 2022
Starlight is a JS engine in Rust which focuses on performance rather than ensuring 100% safety of JS runtime.

starlight Starlight is a JS engine in Rust which focuses on performance rather than ensuring 100% safety of JS runtime. Features Bytecode interpreter

null 453 Dec 31, 2022
Proxies all incoming connections to a minecraft server of your choosing, while also logging all ping and login requests to a json file and discord webhook.

minecraft-honeypot Proxies all incoming connections to a minecraft server of your choosing, while also logging all ping and login requests to a json f

Cleo 19 Jan 4, 2023
The simplest build-time framework for writing web apps with html templates and typescript

Encoped A build-time fast af tool to write static apps with html and TypeScript Features Template-based ESLint, Prettier and Rollup integration No ext

null 1 Dec 11, 2021
HTTP Proxy based solution for real-time interception and prioritization of SQL queries.

starproxy ⚠️ starproxy is a prototype: Not currently used in production, but will likely be some day. Table of Contents starproxy Table of Contents Ba

Will Eaton 5 Mar 6, 2023
☁ file.AsyncWrite - because THEY wont do it!!!!!! and its in RUST

☁ gm_async_write Simple module that adds file.AsyncWrite and file.AsyncAppend to Garry's Mod. These functions are mostly based off file.AsyncRead and

William 18 Dec 24, 2022
Tools that parsing Rust code into UML diagram (in dot format currently).

rudg Rust UML Diagram Generator Tools that parsing Rust code into UML diagram (in dot format currently). Usage $ rudg.exe --help rudg 0.1.0 USAGE:

Zhai Yao 16 Nov 13, 2022
Turn GitHub into an RSS reader

NotCraft::NotFeed An RSS reader running entirely from your GitHub repo. Free hosting on GitHub Pages. No ads. No third party tracking. No need for bac

NotCraft 22 Nov 30, 2022
ditch poetry for pip. converts pyproject.toml into requirements.txt.

Say No To Poetry Did you grow up using just pip or conda? Are you being forced to use poetry everywhere at work? They try to convince you poetry just

Jaivarsan 7 Nov 6, 2022
Merge multiple Juniper object definitions into a single object type.

juniper-compose Merge multiple Juniper object definitions into a single object type. crates.io | docs | github Motivation You are building a GraphQL s

Kit Isaev 3 Aug 5, 2022
Turn any web page into a desktop app (but, lightweight <1MB)

Intro Turn any web page into a desktop app (but, lightweight <1MB) The bundle will be less than 2MB Demo: https://i.imgur.com/BLr03oF.mp4 Install carg

null 9 Dec 27, 2022
Deserialize (potentially nested) environment variables into your custom structs

envious allows you to deserialize your serde enabled structs from environment variables. See it in action: use serde::{Deserialize, Serialize}; #[der

Marcel Müller 46 Feb 19, 2023
Real-time bidding API scaffold for MevWallet transactions

MevWallet RTB API This repo provides a standard interface for a real-time bidding API. Searchers run the API to provide bids to users. The bid represe

Blunt Instruments 17 Mar 23, 2023
Autumn is the microservice responsible for storing files and attachments.

Autumn Description Autumn is the microservice responsible for storing files and attachments. Features: Save files locally or on S3. Support for differ

Revolt 30 Dec 26, 2022