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};
use std::collections::HashMap;
use std::error::Error;
use std::io::Write;
use std::path::PathBuf;
use std::time::SystemTime;
use tabwriter::TabWriter;
pub mod cmd;
pub mod commands;
pub mod config;
pub mod configure;
pub mod ninja;
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(())
}
}
}