use std::collections::HashMap;
use std::error::Error;
use std::io::{Write};
use std::path::{PathBuf};
use std::time::SystemTime;
use tabwriter::TabWriter;
use crate::cmd::enforce_commands;
use crate::commands::api::{build_api, build_api_prod, run_api, run_api_prod};
use crate::commands::assets::build_assets;
use crate::commands::clean::{clean};
use crate::commands::client::{build_client_prod, client_protobuf, run_http};
use crate::commands::docker::{build_docker, build_docker_api, build_docker_api_beta, build_docker_api_stable, build_docker_beta, build_docker_server, build_docker_server_beta, build_docker_server_stable, build_docker_stable, build_docker_web, build_docker_web_beta, build_docker_web_stable};
use crate::commands::server::{build_server, build_server_prod, run_server, run_server_prod};
pub mod commands;
pub mod ninja;
pub mod cmd;
pub mod configure;
pub mod config;
fn main() {
let mut bcm = BuildCommandManager::new();
bcm.register("run_http", Box::new(run_http), "Compile the client and then run a development HTTP server", false);
bcm.register("build_assets", Box::new(build_assets), "Compile the asset source files into spritesheets", false);
bcm.register("client_protobuf", Box::new(client_protobuf), "Update the client protocol bindings", false);
bcm.register("clean", Box::new(clean), "Remove all compilation artifacts", false);
bcm.register("build_api", Box::new(build_api), "Compile the API server", false);
bcm.register("run_api", Box::new(run_api), "Run the API server", false);
bcm.register("build_api_prod", Box::new(build_api_prod), "Compile the API server with optimizations", false);
bcm.register("run_api_prod", Box::new(run_api_prod), "Run the API server with optimizations", false);
bcm.register("build_server", Box::new(build_server), "Compile the game server", false);
bcm.register("run_server", Box::new(run_server), "Run the game server", false);
bcm.register("build_server_prod", Box::new(build_server_prod), "Compile the game server with optimizations", false);
bcm.register("run_server_prod", Box::new(run_server_prod), "Run the game server with optimizations", false);
bcm.register("build_docker_beta", Box::new(build_docker_beta), "Build all three docker images for the beta channel", false);
bcm.register("build_docker_api_beta", Box::new(build_docker_api_beta), "Build the API docker image for the beta channel", false);
bcm.register("build_docker_server_beta", Box::new(build_docker_server_beta), "Build the main docker image for the beta channel", false);
bcm.register("build_docker_web_beta", Box::new(build_docker_web_beta), "Build the webserver docker image for the beta channel", false);
bcm.register("build_docker_stable", Box::new(build_docker_stable), "Build all three docker images for the stable channel", false);
bcm.register("build_docker_api_stable", Box::new(build_docker_api_stable), "Build the API docker image for the stable channel", false);
bcm.register("build_docker_server_stable", Box::new(build_docker_server_stable), "Build the main docker image for the stable channel", false);
bcm.register("build_docker_web_stable", Box::new(build_docker_web_stable), "Build the webserver docker image for the stable channel", false);
bcm.register("build_docker", Box::new(build_docker), "Build all three docker images for the bleeding channel", false);
bcm.register("build_docker_api", Box::new(build_docker_api), "Build the API docker image for the bleeding channel", false);
bcm.register("build_docker_server", Box::new(build_docker_server), "Build the main docker image for the bleeding channel", false);
bcm.register("build_docker_web", Box::new(build_docker_web), "Build the webserver docker image for the bleeding channel", false);
bcm.register("build_client_prod", Box::new(build_client_prod), "Build the production-ready client bundle", false);
let start = SystemTime::now();
let args: Vec<String> = std::env::args().collect();
match bcm.exec(args) {
Ok(_) => (),
Err(e) => {
let end = SystemTime::now();
let duration = end.duration_since(start).unwrap();
println!("[spacetime] Done in {} seconds", duration.as_secs_f32());
eprintln!("[!] Error executing build command: {}", e);
std::process::exit(-1);
}
}
let end = SystemTime::now();
let duration = end.duration_since(start).unwrap();
println!("[spacetime] Done in {} seconds", duration.as_secs_f32());
}
type BuildCommandCallback = Box<dyn Fn(Vec<String>, PathBuf) -> Result<(), Box<dyn Error>>>;
#[derive(Default)]
pub struct BuildCommandManager {
commands: HashMap<String, (BuildCommandCallback, String, bool)>
}
impl BuildCommandManager {
pub fn new() -> Self {
Self {
commands: HashMap::new()
}
}
pub fn register(&mut self, name: &str, command: BuildCommandCallback, description: &str, hidden: bool) {
self.commands.insert(name.to_string(), (command, description.to_string(), hidden));
}
pub fn help(&self) {
println!("Spacetime - StarKingdoms build utility");
println!("Spacetime is a small Rust utility to assist in compiling StarKingdoms.");
println!("Available targets:");
let mut tw = TabWriter::new(vec![]);
for (target, (_, description, hidden)) in &self.commands {
if *hidden { continue };
writeln!(tw, "\t{}\t{}", target, description).unwrap();
}
std::io::stdout().write_all(&tw.into_inner().unwrap()).unwrap();
}
pub fn exec(&self, args: Vec<String>) -> Result<(), Box<dyn Error>> {
if args.len() < 2 || args[1] == "help" || args[1] == "h" {
self.help();
return Ok(())
}
enforce_commands();
let main_path = PathBuf::from(args[2].to_string());
if let Some((callback, _, _)) = self.commands.get(&args[1]) {
callback(args[2..].to_owned(), main_path)
} else {
eprintln!("Unrecognized build command {}", args[1]);
self.help();
Ok(())
}
}
}