use base64::Engine;
use std::{
env,
fs::{self, File},
io::{Read, Write},
net::TcpListener,
path::PathBuf,
};
fn main() {
let args: Vec<String> = env::args().collect();
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".to_string());
let html_path = PathBuf::from(&manifest_dir).join("target/index.html");
let js_path = PathBuf::from(&manifest_dir).join("target/hecatomb.js");
if args.len() > 1 && args[1] == "serve" {
let mut port = 10000;
if args.len() > 2 {
if let Ok(p) = args[2].parse::<u16>() {
port = p;
} else if args[2] == "--port" || args[2] == "-p" {
if args.len() > 3 {
if let Ok(p) = args[3].parse::<u16>() {
port = p;
}
}
}
}
println!(
"Serving {} on http://127.0.0.1:{}",
html_path.display(),
port
);
let listener = match TcpListener::bind(format!("127.0.0.1:{}", port)) {
Ok(l) => l,
Err(e) => {
eprintln!("Error binding to port {}: {}", port, e);
std::process::exit(1);
}
};
for stream in listener.incoming() {
let mut stream = match stream {
Ok(s) => s,
Err(_) => continue,
};
let mut buffer = [0; 4096];
let n = stream.read(&mut buffer).unwrap_or(0);
let request = String::from_utf8_lossy(&buffer[..n]);
let path = request
.lines()
.next()
.and_then(|line| line.split_whitespace().nth(1))
.unwrap_or("/");
let (file_path, content_type) = if path == "/hecatomb.js" {
(&js_path, "text/javascript; charset=utf-8")
} else {
(&html_path, "text/html; charset=utf-8")
};
match fs::read(file_path) {
Ok(content) => {
let response = format!(
"HTTP/1.1 200 OK\r\n\
Content-Type: {}\r\n\
Content-Length: {}\r\n\
Connection: close\r\n\r\n",
content_type,
content.len()
);
let _ = stream.write_all(response.as_bytes());
let _ = stream.write_all(&content);
}
Err(e) => {
let error_msg = format!(
"Error: file not found at {}. Run build first. Details: {}",
file_path.display(),
e
);
let response = format!(
"HTTP/1.1 404 NOT FOUND\r\n\
Content-Type: text/plain; charset=utf-8\r\n\
Content-Length: {}\r\n\
Connection: close\r\n\r\n{}",
error_msg.len(),
error_msg
);
let _ = stream.write_all(response.as_bytes());
}
}
let _ = stream.flush();
}
return;
}
println!("Building");
let wasm_path =
PathBuf::from(&manifest_dir).join("target/wasm32-unknown-unknown/release/hecatomb.wasm");
let Ok(mut wasm_file) = File::options().read(true).open(&wasm_path) else {
panic!("could not locate wasm file");
};
// turn file into base64
let mut buffer = Vec::new();
wasm_file.read_to_end(&mut buffer).unwrap();
let base64 = base64::engine::general_purpose::STANDARD.encode(&buffer);
// print out base64
let first_ten_chars: String = base64.chars().take(10).collect();
println!("base64 created {}", first_ten_chars);
//inline wasm into the js bundle (served separately, not inlined into html)
let js_template = include_str!("template.js");
let js = js_template.replace("{{}}", base64.as_str());
std::fs::write(&js_path, js).unwrap();
println!("Wrote {}", js_path.display());
// html loads the js bundle from the server via <script src="/hecatomb.js">
let html = include_str!("template.html");
std::fs::write(&html_path, html).unwrap();
println!("Wrote {}", html_path.display());
}