use clap::{Args, Parser, Subcommand};
use xbp::commands::curl;
use xbp::commands::redeploy_v2::run_redeploy_v2;
use xbp::commands::{install_package, run_config, run_ports, run_redeploy, run_setup};
use xbp::commands::{pm2_list, pm2_logs};
use xbp::commands::{list_services, run_service_command, show_service_help, is_xbp_project, run_redeploy_service};
use xbp::logging::{init_logger, log_error, log_info, log_success};
#[derive(Parser, Debug)]
#[command(
name = "xbp",
version,
about = "XBP CLI",
disable_help_subcommand = false
)]
struct Cli {
#[arg(long, global = true)]
debug: bool,
#[arg(short = 'l', help = "List pm2 processes")]
list: bool,
#[arg(short = 'p', long = "port", help = "Filter by port number")]
port: Option<u16>,
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand, Debug)]
enum Commands {
Ports(PortsCmd),
Setup,
Redeploy {
#[arg(help = "Service name to redeploy (optional, uses legacy redeploy.sh if not provided)")]
service_name: Option<String>,
},
RedeployV2(RedeployV2Cmd),
Config,
Install {
package: String,
},
Logs(LogsCmd),
List,
Curl {
#[arg(help = "URL to fetch, e.g. https://example.com/api")]
url: Option<String>,
},
Services,
Service {
#[arg(help = "Command to run: build, install, start, dev, or --help")]
command: Option<String>,
#[arg(help = "Service name")]
service_name: Option<String>,
},
}
#[derive(Args, Debug)]
struct PortsCmd {
#[arg(short = 'p', long = "port")]
port: Option<u16>,
#[arg(long = "kill")]
kill: bool,
#[arg(short = 'n')]
nginx: bool,
}
#[derive(Args, Debug)]
struct RedeployV2Cmd {
#[arg(short = 'p', long = "password")]
password: Option<String>,
#[arg(short = 'u', long = "username")]
username: Option<String>,
#[arg(short = 'h', long = "host")]
host: Option<String>,
#[arg(short = 'd', long = "project-dir")]
project_dir: Option<String>,
}
#[derive(Args, Debug)]
struct LogsCmd {
#[arg()]
project: Option<String>,
}
#[tokio::main]
async fn main() {
let cli = Cli::parse();
let debug = cli.debug;
if let Err(e) = init_logger(debug).await {
let _ = log_error(
"system",
"Failed to initialize logger",
Some(&e.to_string()),
)
.await;
}
if cli.list && cli.command.is_none() {
if let Err(e) = pm2_list(debug).await {
let _ = log_error("pm2", "pm2 list failed", Some(&e)).await;
}
return;
}
match cli.command {
Some(Commands::Ports(cmd)) => {
let mut sim_args: Vec<String> = Vec::new();
let port = cli.port.or(cmd.port);
if let Some(p) = port {
sim_args.push("-p".to_string());
sim_args.push(p.to_string());
}
if cmd.kill {
sim_args.push("--kill".to_string());
sim_args.push("-k".to_string());
}
if cmd.nginx {
sim_args.push("-n".to_string());
sim_args.push("--nginx".to_string());
}
if let Err(e) = run_ports(&sim_args, debug).await {
let _ = log_error("ports", "Error running ports", Some(&e)).await;
}
}
Some(Commands::Setup) => {
if let Err(e) = run_setup(debug).await {
let _ = log_error("setup", "Setup failed", Some(&e)).await;
}
}
Some(Commands::Redeploy { service_name }) => {
if let Some(name) = service_name {
if let Err(e) = run_redeploy_service(&name, debug).await {
let _ = log_error("redeploy", "Service redeploy failed", Some(&e)).await;
}
} else {
if let Err(e) = run_redeploy().await {
let _ = log_error("redeploy", "Redeploy failed", Some(&e)).await;
}
}
}
Some(Commands::RedeployV2(cmd)) => {
let _ = log_info("redeploy_v2", "Starting remote redeploy process", None).await;
match run_redeploy_v2(cmd.password, cmd.username, cmd.host, cmd.project_dir, debug)
.await
{
Ok(()) => {}
Err(e) => {
let _ = log_error("redeploy_v2", "Remote redeploy failed", Some(&e)).await;
}
}
}
Some(Commands::Config) => {
let _ = run_config(debug).await;
}
Some(Commands::Install { package }) => {
let install_msg = format!("Installing package: {}", package);
let _ = log_info("install", &install_msg, None).await;
match install_package(&package, debug).await {
Ok(()) => {
let success_msg = format!("Successfully installed: {}", package);
let _ = log_success("install", &success_msg, None).await;
}
Err(e) => {
let error_msg = format!("Failed to install: {}", package);
let _ = log_error("install", &error_msg, Some(&e)).await;
}
}
}
Some(Commands::Logs(cmd)) => {
if let Err(e) = pm2_logs(cmd.project, debug).await {
let _ = log_error("pm2", "pm2 logs failed", Some(&e)).await;
}
}
Some(Commands::List) => {
if let Err(e) = pm2_list(debug).await {
let _ = log_error("pm2", "pm2 list failed", Some(&e)).await;
}
}
Some(Commands::Curl { url }) => {
let url = url.unwrap_or_else(|| "https://example.com/api".to_string());
if let Err(e) = curl::run_curl(&url, debug).await {
let _ = log_error("curl", "Curl command failed", Some(&e)).await;
}
}
Some(Commands::Services) => {
if let Err(e) = list_services(debug).await {
let _ = log_error("services", "Failed to list services", Some(&e)).await;
}
}
Some(Commands::Service { command, service_name }) => {
if let Some(cmd) = command {
if cmd == "--help" || cmd == "help" {
if let Some(name) = service_name {
if let Err(e) = show_service_help(&name).await {
let _ = log_error("service", "Failed to show service help", Some(&e)).await;
}
} else {
println!("Usage: xbp service <command> <service-name>");
println!("Commands: build, install, start, dev");
println!("Example: xbp service build zeus");
println!("For help on a specific service: xbp service --help <service-name>");
}
} else {
if let Some(name) = service_name {
if let Err(e) = run_service_command(&cmd, &name, debug).await {
let _ = log_error("service", &format!("Service command '{}' failed", cmd), Some(&e)).await;
}
} else {
let _ = log_error("service", "Service name required", None).await;
}
}
} else {
println!("Usage: xbp service <command> <service-name>");
println!("Commands: build, install, start, dev");
println!("Example: xbp service build zeus");
}
}
None => {
if let Some(port) = cli.port {
let sim_args = vec!["-p".to_string(), port.to_string()];
if let Err(e) = run_ports(&sim_args, debug).await {
let _ = log_error("ports", "Error running ports", Some(&e)).await;
}
return;
}
if is_xbp_project().await {
println!("XBP Project Detected\n");
if let Err(e) = list_services(debug).await {
let _ = log_error("services", "Failed to list services", Some(&e)).await;
}
println!("\nAvailable Commands:");
println!(" xbp services - List all services");
println!(" xbp service <cmd> <name> - Run command for a service");
println!(" xbp redeploy <name> - Redeploy a service");
println!(" xbp config - Show configuration");
println!("\nFor more help: xbp --help");
} else {
let _ = log_error(
"system",
"No subcommand provided. Usage: xbp [SUBCOMMAND] | xbp -l | xbp -p <port>",
None,
)
.await;
}
}
}
}