diff --git a/README.md b/README.md index 0c77814..ac1317a 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ memory-serve is designed to work with [axum](https://github.com/tokio-rs/axum) ## Usage -Provide a relative path to the directory containing your static assets +Provide a relative or absolute path to the directory containing your static assets using the `ASSET_PATH` environment variable. The path will be used in a build script, that creates a data structure intended to be consumed by [`MemoryServe::new`]. Calling [`MemoryServe::into_router()`] on @@ -42,6 +42,12 @@ the resulting instance produces a axum can either be merged in another `Router` or used directly in a server by calling [`Router::into_make_service()`](https://docs.rs/axum/latest/axum/routing/struct.Router.html#method.into_make_service). +### Named directories + +Multiple directories can be included using different environment variables, all prefixed by `ASSET_PATH_`. +For example: if you specify `ASSET_PATH_FOO` and `ASSET_PATH_BAR` the memory serve instances can be loaded +using `MemoryServe::from_name("FOO")` and `MemoryServe::from_name("BAR")` respectively. + ## Example ```rust,no_run diff --git a/build.rs b/build.rs index a5ace4e..afcc3d4 100644 --- a/build.rs +++ b/build.rs @@ -181,10 +181,10 @@ pub fn list_assets(base_path: &Path, embed: bool) -> Vec { assets } -const ASSET_FILE: &str = "memory_serve_assets"; +const ASSET_FILE: &str = "memory_serve_assets.rs"; const ENV_NAME: &str = "ASSET_DIR"; -fn include_directory(asset_dir: &str, path: &Path, out_dir: &Path, embed: bool, name: &str) { +fn include_directory(asset_dir: &str, path: &Path, out_dir: &Path, embed: bool) -> String { log!("Loading static assets from {asset_dir}"); let assets = list_assets(path, embed); @@ -229,18 +229,9 @@ fn include_directory(asset_dir: &str, path: &Path, out_dir: &Path, embed: bool, code.push(']'); - log!("NAME {name}"); - - let target = if name == ENV_NAME { - Path::new(&out_dir).join(format!("{ASSET_FILE}.rs")) - } else { - Path::new(&out_dir).join(format!("{ASSET_FILE}_{name}.rs")) - }; - - std::fs::write(target, code).expect("Unable to write memory-serve asset file."); - println!("cargo::rerun-if-changed={asset_dir}"); - println!("cargo::rerun-if-env-changed={name}"); + + code } fn resolve_asset_dir(out_dir: &Path, key: &str, asset_dir: &str) -> PathBuf { @@ -289,21 +280,29 @@ fn main() { let mut found = false; + // using a string is faster than using quote ;) + let mut code = "&[".to_string(); + for (key, asset_dir) in std::env::vars() { if key.starts_with(ENV_NAME) { let name = key.trim_start_matches(format!("{ENV_NAME}_").as_str()); let path = resolve_asset_dir(&out_dir, &key, &asset_dir); - include_directory(&asset_dir, &path, &out_dir, embed, name); + let assets = include_directory(&asset_dir, &path, &out_dir, embed); + code = format!("{code}(\"{name}\", {assets}),"); found = true; } } if !found { - let target = Path::new(&out_dir).join(format!("{ASSET_FILE}.rs")); log!("Please specify the `{ENV_NAME}` environment variable."); - std::fs::write(target, "&[]").expect("Unable to write memory-serve asset file."); - println!("cargo::rerun-if-env-changed=ASSET_DIR"); - }; + } + + code.push(']'); + + println!("cargo::rerun-if-env-changed={ENV_NAME}"); + + let target = out_dir.join(ASSET_FILE); + std::fs::write(target, code).expect("Unable to write memory-serve asset file."); } diff --git a/src/lib.rs b/src/lib.rs index 8a7d0eb..5ff72e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,7 +56,39 @@ impl MemoryServe { /// created at build time. /// Specify which asset directory to include using the environment variable `ASSET_DIR`. pub fn new() -> Self { - let assets: &[Asset] = include!(concat!(env!("OUT_DIR"), "/memory_serve_assets.rs")); + let assets: &[(&str, &[Asset])] = + include!(concat!(env!("OUT_DIR"), "/memory_serve_assets.rs")); + + if assets.is_empty() { + panic!("No assets found, did you forget to set the ASSET_DIR environment variable?"); + } + + Self { + assets: assets[0].1, + ..Default::default() + } + } + + /// Include a directory using a named environment variable, prefixed by ASSRT_DIR_. + /// Specify which asset directory to include using the environment variable `ASSET_DIR_`. + /// The name should be in uppercase. + /// For example to include assets from the public directory using the name PUBLIC, set the enirobment variable + /// `ASSET_DIR_PUBLIC=./public` and call `MemoryServe::from_name("PUBLIC")`. + pub fn from_name(name: &str) -> Self { + let assets: &[(&str, &[Asset])] = + include!(concat!(env!("OUT_DIR"), "/memory_serve_assets.rs")); + + let assets = assets + .iter() + .find(|(n, _)| n == &name) + .map(|(_, a)| *a) + .unwrap_or_default(); + + if assets.is_empty() { + panic!( + "No assets found, did you forget to set the ASSET_DIR_{name} environment variable?" + ); + } Self { assets, @@ -295,7 +327,9 @@ mod tests { #[test] fn test_load_assets() { - let assets: &[Asset] = include!(concat!(env!("OUT_DIR"), "/memory_serve_assets.rs")); + let assets_list: &[(&str, &[Asset])] = + include!(concat!(env!("OUT_DIR"), "/memory_serve_assets.rs")); + let assets = assets_list[0].1; let routes: Vec<&str> = assets.iter().map(|a| a.route).collect(); let content_types: Vec<&str> = assets.iter().map(|a| a.content_type).collect(); let etags: Vec<&str> = assets.iter().map(|a| a.etag).collect();