cacheb 0.1.0

Compile time cache busting for static assets in web applications.
Documentation
  • Coverage
  • 0%
    0 out of 2 items documented0 out of 1 items with examples
  • Size
  • Source code size: 21.99 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 274.33 kB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 9s Average build duration of successful builds.
  • all releases: 9s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Homepage
  • beanpuppy/cacheb
    1 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • beanpuppy

cacheb

Compile time cache busting for static assets in web applications.

Features

  • Content-based hashes ensure browsers load the latest version
  • Seamless integration with templates
  • Files are organized in modules matching your directories
  • Compiler catches typos in file references

Example usage

Add cacheb to [build-dependencies].

[build-dependencies]
cacheb = "0.1"

Create a build.rs to trigger cacheb whenever your static files change.

fn main() {
    println!("cargo:rerun-if-changed=static/");
    cacheb::codegen(
        &PathBuf::from("src/static_assets.rs"),
        &[PathBuf::from("static")],
        &[]
    ).unwrap();
}

Reference your static assets in templates (eg. Maud) with automatic cache busting:

mod static_assets;
use static_assets::*;

fn page() -> maud::Markup {
    html! {
        head {
            link rel="stylesheet" href=(styles::main_css);
            link rel="icon" href=(favicon_ico);
        }
        body {
            img src=(images::logo_png) alt="Logo";
            script src=(scripts::app_js) {}
        }
    }
}

Implement a handler to serve your static files:

#[derive(TypedPath, Deserialize)]
#[typed_path("/static/{*path}")]
pub struct StaticFilePath {
    pub path: String,
}

pub async fn static_path(StaticFilePath { path }: StaticFilePath) -> impl IntoResponse {
    let data = StaticFile::get(&format!("/static/{}", path));

    if let Some(data) = data {
        let file = match tokio::fs::File::open(data.file_name).await {
            Ok(file) => file,
            Err(_) => {
                return Response::builder()
                    .status(StatusCode::NOT_FOUND)
                    .body(Body::empty())
                    .unwrap();
            }
        };

        return Response::builder()
            .status(StatusCode::OK)
            .header(
                header::CONTENT_TYPE,
                HeaderValue::from_str(data.mime.as_ref()).unwrap(),
            )
            .body(Body::from_stream(ReaderStream::new(file)))
            .unwrap();
    }

    Response::builder()
        .status(StatusCode::NOT_FOUND)
        .body(Body::empty())
        .unwrap()
}

let app = Router::new()
    .route("/static/{*path}", get(static_path));