[][src]Crate reinda

This library helps with easily including and serving assets (like JS or CSS files) in your web application. It is fairly configurable and supports a variety of features. In particular, it can embed all assets into your executable at compile time to get an easy to deploy standalone-executable.

Quick start

To use reinda, you mostly need to do three things: (1) define your assets with assets!, (2) create an Assets instance, (3) call Assets::get to serve your asset.

use reinda::{assets, Assets, Config, Setup};

const ASSETS: Setup = assets! {
    // Folder which contains your assets, relative to your `Cargo.toml`.
    #![base_path = "assets"]

    // List of assets to include, with different settings.
    "index.html": { template },
    "bundle.js": { hash },
};


#[tokio::main]
async fn main() -> Result<(), reinda::Error> {
    // Initialize assets
    let assets = Assets::new(ASSETS, Config::default()).await?;

    // Retrieve specific asset. You can now send this data via HTTP or use
    // it however you like.
    let bytes /*: Option<bytes::Bytes> */ = assets.get("index.html").await?;

    Ok(())
}

The hash keyword in the macro invocation means that bundle.js will be obtainable only with a filename that contains a hash of its content, e.g. bundle.JdeK1YeQ90aJ.js. This is useful for caching on the web: you can now serve the bundle.js with a very large max-age in the cache-control header. Whenever your asset changes, the URI changes as well, so the browser has to re-request it.

But how do you include the correct JS bundle path in your HTML file? That's what template is for. reinda supports very basic templating. If you define your HTML file like this:

<html>
  <head></head>
  <body>
    <script src="{{: path:bundle.js :}}" />
  </body>
</html>

Then the {{: ... :}} part will be replaced by the actual, hashed path of bundle.js. There are more uses for the template, as you can see below.

To learn more about this library, keep reading, or check out the docs for assets! for information on asset specification, or checkout Config for information about runtime configuration.

Embed or loaded at runtime: dev vs. prod mode

This library has two different modes: dev (short for development) and prod (short for production). The name "prod" is deliberately not the same as "release" (the typical Rust term for it), because those two are not always the same.

There are several differences between the two modes:

dev modeprod mode
Normal assetsLoaded from filesystem when requestedEmbedded into binary
dynamic: true assetsLoaded from filesystem when requestedLoaded in Assets::new
hash: true assetsfilename not modifiedhash inserted into filename
Base pathconfig.base_path with current workdir as fallbackGiven via #![base_path]

By default, if you compile in Cargo debug mode (e.g. cargo build), dev mode is used. If you compile in Cargo's release mode (e.g. cargo build --release), prod mode is used. You can instruct reinda to always use prod mode by enabling the feature debug-is-prod:

reinda = { version = "...", features = ["debug-is-prod"] }

Template

reinda has a simple template system. The input file is checked for "fragments" which have the syntax {{: foo :}}. The start token is actually {{: (note the whitespace!). So {{:foo:}} is not recognized as fragment. The syntax was chosen to not conflict with other template syntax that might be present in the asset files. Please let me know if some other template engine out there uses the {{: syntax! Then I might change the syntax of reinda.

Inside a fragment, there are different replacement functions you can use:

  • include: allows you to include the content of another file in place of the template fragment. If the included file is a template as well, that will be rendered before being included. Example: {{: include:colors.css }}.

  • path: replaces the fragment with the potential hashed path of another asset. This only makes sense for hashed asset paths as otherwise you could just insert the path directly. Example: {{: path:bundle.js :}}.

  • var: replaces the fragment with a runtime provided variable. See Config::variables. Example: {{: var:main-color :}}.

Fragments have two other intended limitations: they must not contain a newline and must not be longer than 256 characters. This is to further prevent the template accidentally picking up start tokens that are not intended for reinda.

Example

index.html:

<html>
  <head>
    <script type="application/json">{ "accentColor": "{{: var:color :}}" }</script>
    <style>{{: include:style.css :}}</style>
  </head>
  <body>
    <script src="{{: path:bundle.js :}}" />
  </body>
</html>

style.css

body {
  margin: 0;
}

And assuming bundle.js was declared with hash (to hash its filename) and the config.variables contained the entry "color": "blue", then the resulting index.html looks like this:

<html>
  <head>
    <script type="application/json">{ "accentColor": "blue" }</script>
    <style>body {
  margin: 0;
}</style>
  </head>
  <body>
    <script src="bundle.JdeK1YeQ90aJ.js" />
  </body>
</html>

Cargo features

  • compress (enabled by default): if enabled, embedded files are compressed. This often noticably reduces the binary size of the executable. This feature adds the flate2 dependency.

  • hash (enabled by default): is required for support of filename hashing (see above). This feature adds the bas64 and sha2 dependencies.

  • debug-is-prod: see the section about "prod" and "dev" mode above.

Notes, Requirements and Limitations

  • reinda actually consists of three crates: reinda-core, reinda-macros and the main crate. To detect whether Cargo compiles in debug or release mode, cfg(debug_assertions) is used. All three of these crates have to be compiled with with the same setting regarding debug assertions, otherwise you will either see strange compile errors or strange runtime behavior (no UB though). This shouldn't be a concern, as all crates in your dependency graph are compiled with the same codegen settings, unless you include per-dependency overrides in your Cargo.toml. So just don't do that.
  • The environment variable CARGO_MANIFEST_DIR has to be set when expanding the assets! macro. Cargo does this automatically. But if you, for some reason, compile manually with rustc, you have to set that value.

Macros

assets

Compile time configuration of assets. Returns a Setup.

Structs

AssetId

Simple ID to refer to one asset in a Setup or Assets struct.

Assets

A set of assets.

Config

Runtime configuration.

Info

Contains meta information about an asset.

Setup

An opaque structure that holds metadata and (in prod mode) the included raw asset data.

Enums

Error

All errors that might be returned by reinda.

Type Definitions

GetError

Error type for Assets::get, which is different for dev and prod builds.