From 2ed77bf007225547647b916cacd09337c0e6dd05 Mon Sep 17 00:00:00 2001 From: Tim Jentzsch Date: Mon, 23 Dec 2024 11:33:57 +0100 Subject: [PATCH] Serve app from bundle info --- src/run/bundle.rs | 38 +++++++++++++++--------- src/run/mod.rs | 12 +++++++- src/run/serve.rs | 75 ++++++++++++++++++++++++++--------------------- 3 files changed, 77 insertions(+), 48 deletions(-) diff --git a/src/run/bundle.rs b/src/run/bundle.rs index 3a68eb5..8bdc25b 100644 --- a/src/run/bundle.rs +++ b/src/run/bundle.rs @@ -1,26 +1,32 @@ use std::{ + ffi::OsString, fs, path::{Path, PathBuf}, }; use super::{cargo::metadata::Metadata, BinTarget}; +#[derive(Debug, Clone)] pub enum Index { - File(PathBuf), + Folder(PathBuf), Static(&'static str), } +#[derive(Debug, Clone)] pub struct LinkedBundle { - wasm_path: PathBuf, - js_path: PathBuf, - assets_path: Option, - index: Index, + pub build_artifact_path: PathBuf, + pub wasm_file_name: OsString, + pub js_file_name: OsString, + pub assets_path: Option, + pub index: Index, } +#[derive(Debug, Clone)] pub struct PackedBundle { - path: PathBuf, + pub path: PathBuf, } +#[derive(Debug, Clone)] pub enum WebBundle { Linked(LinkedBundle), Packed(PackedBundle), @@ -35,12 +41,13 @@ pub fn create_web_bundle( let assets_path = Path::new("assets"); // The "_bg" suffix is needed to reference the bindings created by wasm_bindgen, // instead of the artifact created directly by cargo. - let wasm_file_name = format!("{}_bg.wasm", bin_target.bin_name); - let js_file_name = format!("{}.js", bin_target.bin_name); + let wasm_file_name = OsString::from(format!("{}_bg.wasm", bin_target.bin_name)); + let js_file_name = OsString::from(format!("{}.js", bin_target.bin_name)); let linked = LinkedBundle { - wasm_path: bin_target.artifact_directory.join(&wasm_file_name), - js_path: bin_target.artifact_directory.join(&js_file_name), + build_artifact_path: bin_target.artifact_directory.clone(), + wasm_file_name, + js_file_name, assets_path: if assets_path.exists() { Some(assets_path.to_owned()) } else { @@ -63,10 +70,13 @@ pub fn create_web_bundle( // Build artifacts fs::create_dir_all(base_path.join("build"))?; fs::copy( - linked.wasm_path, - base_path.join("build").join(&wasm_file_name), + linked.build_artifact_path.join(&linked.wasm_file_name), + base_path.join("build").join(&linked.wasm_file_name), + )?; + fs::copy( + linked.build_artifact_path.join(&linked.js_file_name), + base_path.join("build").join(&linked.js_file_name), )?; - fs::copy(linked.js_path, base_path.join("build").join(&js_file_name))?; // Assets if let Some(assets_path) = linked.assets_path { @@ -83,7 +93,7 @@ pub fn create_web_bundle( // Index let index_path = base_path.join("index.html"); match linked.index { - Index::File(path) => { + Index::Folder(path) => { fs::copy(path, index_path)?; } Index::Static(contents) => { diff --git a/src/run/mod.rs b/src/run/mod.rs index c671f4d..626a468 100644 --- a/src/run/mod.rs +++ b/src/run/mod.rs @@ -1,6 +1,8 @@ use std::path::PathBuf; +use anyhow::Context; use args::RunSubcommands; +use bundle::create_web_bundle; use crate::{ build::ensure_web_setup, @@ -39,6 +41,14 @@ pub fn run(args: &RunArgs) -> anyhow::Result<()> { )?; wasm_bindgen::bundle(&bin_target)?; + let web_bundle = create_web_bundle( + &metadata, + args.profile(), + bin_target, + web_args.create_bundle, + ) + .context("Failed to create web bundle")?; + let port = web_args.port; let url = format!("http://localhost:{port}"); @@ -54,7 +64,7 @@ pub fn run(args: &RunArgs) -> anyhow::Result<()> { println!("Open your app at <{url}>!"); } - serve::serve(bin_target, port)?; + serve::serve(web_bundle, port)?; } else { // For native builds, wrap `cargo run` cargo::run::command().args(cargo_args).ensure_status()?; diff --git a/src/run/serve.rs b/src/run/serve.rs index daf1367..e4ed2ec 100644 --- a/src/run/serve.rs +++ b/src/run/serve.rs @@ -1,8 +1,7 @@ //! Serving the app locally for the browser. use actix_web::{rt, web, App, HttpResponse, HttpServer, Responder}; -use std::path::Path; -use super::{bundle::default_index, BinTarget}; +use super::bundle::{LinkedBundle, PackedBundle, WebBundle}; /// Serve a static HTML file with the given content. async fn serve_static_html(content: &'static str) -> impl Responder { @@ -16,41 +15,51 @@ async fn serve_static_html(content: &'static str) -> impl Responder { } /// Launch a web server running the Bevy app. -pub(crate) fn serve(bin_target: BinTarget, port: u16) -> anyhow::Result<()> { - let index_html = default_index(&bin_target); - +pub(crate) fn serve(web_bundle: WebBundle, port: u16) -> anyhow::Result<()> { rt::System::new().block_on( HttpServer::new(move || { let mut app = App::new(); - let bin_target = bin_target.clone(); - - // Serve the build artifacts at the `/build/*` route - // A custom `index.html` will have to call `/build/{bin_name}.js` - app = app.service( - actix_files::Files::new("/build", bin_target.artifact_directory.clone()) - // This potentially includes artifacts which we will not need, - // but we can't add the bin name to the check due to lifetime requirements - .path_filter(move |path, _| { - path.file_stem().is_some_and(|stem| { - // Using `.starts_with` instead of equality, because of the `_bg` suffix - // of the WASM bindings - stem.to_string_lossy().starts_with(&bin_target.bin_name) - }) && (path.extension().is_some_and(|ext| ext == "js") - || path.extension().is_some_and(|ext| ext == "wasm")) - }), - ); - - // If the app has an assets folder, serve it under `/assets` - if Path::new("assets").exists() { - app = app.service(actix_files::Files::new("/assets", "./assets")) - } - if Path::new("web").exists() { - // Serve the contents of the `web` folder under `/`, if it exists - app = app.service(actix_files::Files::new("/", "./web").index_file("index.html")); - } else { - // If the user doesn't provide a custom web setup, serve a default `index.html` - app = app.route("/", web::get().to(|| serve_static_html(index_html))) + match web_bundle.clone() { + WebBundle::Packed(PackedBundle { path }) => { + app = app.service(actix_files::Files::new("/", path).index_file("index.html")); + } + WebBundle::Linked(LinkedBundle { + build_artifact_path, + wasm_file_name, + js_file_name, + index, + assets_path, + }) => { + // Serve the build artifacts at the `/build/*` route + // A custom `index.html` will have to call `/build/{bin_name}.js` + app = app.service( + actix_files::Files::new("/build", build_artifact_path) + // This potentially includes artifacts which we will not need, + // but we can't add the bin name to the check due to lifetime + // requirements + .path_filter(move |path, _| { + path.file_name() == Some(&js_file_name) + || path.file_name() == Some(&wasm_file_name) + }), + ); + + // If the app has an assets folder, serve it under `/assets` + if let Some(assets_path) = assets_path { + app = app.service(actix_files::Files::new("/assets", assets_path)) + } + + match index { + super::bundle::Index::Folder(path) => { + app = app.service( + actix_files::Files::new("/", path).index_file("index.html"), + ); + } + super::bundle::Index::Static(contents) => { + app = app.route("/", web::get().to(move || serve_static_html(contents))) + } + } + } } app