perseus_cli/
serve_exported.rs

1use console::Emoji;
2use std::net::SocketAddr;
3use std::path::PathBuf;
4use warp::Filter;
5
6use crate::{
7    errors::ExecutionError,
8    export_error_page,
9    parse::{ExportErrorPageOpts, Opts},
10    Tools,
11};
12
13static SERVING: Emoji<'_, '_> = Emoji("🛰️ ", "");
14
15/// Serves an exported app, assuming it's already been exported.
16pub async fn serve_exported(
17    dir: PathBuf,
18    host: String,
19    port: u16,
20    tools: &Tools,
21    global_opts: &Opts,
22) -> Result<i32, ExecutionError> {
23    // Export the 404 page so we can serve that directly for convenience (we don't
24    // need to delete this, since we'll just put it in the `dist/exported`
25    // directory)
26    let exit_code = export_error_page(
27        dir.clone(),
28        &ExportErrorPageOpts {
29            code: "404".to_string(),
30            output: "dist/exported/__export_404.html".to_string(),
31        },
32        tools,
33        global_opts,
34        false, // Don't prompt the user
35    )?;
36    if exit_code != 0 {
37        return Ok(exit_code);
38    }
39
40    let dir = dir.join("dist/exported");
41    // We actually don't have to worry about HTML file extensions at all
42    let files = warp::any()
43        .and(warp::fs::dir(dir))
44        .or(warp::fs::file("dist/exported/__export_404.html"));
45    // Parse `localhost` into `127.0.0.1` (picky Rust `std`)
46    let host = if host == "localhost" {
47        "127.0.0.1".to_string()
48    } else {
49        host
50    };
51    // Parse the host and port into an address
52    let addr: SocketAddr = format!("{}:{}", host, port).parse().unwrap();
53    // Notify the user that we're serving their files
54    println!(
55        "  [3/3] {} Your exported app is now live at <http://{host}:{port}>!",
56        SERVING,
57        host = host,
58        port = port
59    );
60
61    warp::serve(files).run(addr).await;
62    // We will never get here (the above runs forever)
63    Ok(0)
64}