use clap::Subcommand;
use std::path::PathBuf;
#[derive(Subcommand)]
pub(crate) enum ChaosCommands {
Profile {
#[command(subcommand)]
profile_command: ProfileCommands,
},
}
#[derive(Subcommand)]
pub(crate) enum ProfileCommands {
Apply {
name: String,
#[arg(long, default_value = "http://localhost:3000")]
base_url: String,
},
Export {
name: String,
#[arg(long, default_value = "json")]
format: String,
#[arg(short, long)]
output: Option<PathBuf>,
#[arg(long, default_value = "http://localhost:3000")]
base_url: String,
},
Import {
#[arg(short, long)]
file: PathBuf,
#[arg(long, default_value = "http://localhost:3000")]
base_url: String,
},
List {
#[arg(long, default_value = "http://localhost:3000")]
base_url: String,
},
}
pub(crate) async fn handle_chaos_command(
chaos_command: ChaosCommands,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
match chaos_command {
ChaosCommands::Profile { profile_command } => match profile_command {
ProfileCommands::Apply { name, base_url } => {
println!("\u{1f527} Applying chaos profile: {}", name);
let client = reqwest::Client::new();
let url = format!("{}/api/chaos/profiles/{}/apply", base_url, name);
let response = client.post(&url).send().await?;
if response.status().is_success() {
println!("\u{2705} Profile '{}' applied successfully", name);
} else {
let error_text = response.text().await.unwrap_or_default();
eprintln!("\u{274c} Failed to apply profile: {}", error_text);
std::process::exit(1);
}
}
ProfileCommands::Export {
name,
format,
output,
base_url,
} => {
println!("\u{1f4e4} Exporting profile: {}", name);
let client = reqwest::Client::new();
let url =
format!("{}/api/chaos/profiles/{}/export?format={}", base_url, name, format);
let response = client.get(&url).send().await?;
if response.status().is_success() {
let content = response.text().await?;
if let Some(output_path) = output {
tokio::fs::write(&output_path, content).await?;
println!("\u{2705} Profile exported to: {}", output_path.display());
} else {
println!("{}", content);
}
} else {
let error_text = response.text().await.unwrap_or_default();
eprintln!("\u{274c} Failed to export profile: {}", error_text);
std::process::exit(1);
}
}
ProfileCommands::Import { file, base_url } => {
println!("\u{1f4e5} Importing profile from: {}", file.display());
let content = tokio::fs::read_to_string(&file).await?;
let format = if file.extension().and_then(|s| s.to_str()) == Some("yaml")
|| file.extension().and_then(|s| s.to_str()) == Some("yml")
{
"yaml"
} else {
"json"
};
let client = reqwest::Client::new();
let url = format!("{}/api/chaos/profiles/import", base_url);
let response = client
.post(&url)
.json(&serde_json::json!({
"content": content,
"format": format
}))
.send()
.await?;
if response.status().is_success() {
println!("\u{2705} Profile imported successfully");
} else {
let error_text = response.text().await.unwrap_or_default();
eprintln!("\u{274c} Failed to import profile: {}", error_text);
std::process::exit(1);
}
}
ProfileCommands::List { base_url } => {
println!("\u{1f4cb} Listing available chaos profiles...");
let client = reqwest::Client::new();
let url = format!("{}/api/chaos/profiles", base_url);
let response = client.get(&url).send().await?;
if response.status().is_success() {
let profiles: Vec<serde_json::Value> = response.json().await?;
println!("\nAvailable Profiles:");
println!("{:-<80}", "");
for profile in profiles {
let name = profile["name"].as_str().unwrap_or("unknown");
let description = profile["description"].as_str().unwrap_or("");
let builtin = profile["builtin"].as_bool().unwrap_or(false);
let tags = profile["tags"]
.as_array()
.map(|arr| {
arr.iter().filter_map(|v| v.as_str()).collect::<Vec<_>>().join(", ")
})
.unwrap_or_default();
println!(
" \u{2022} {} {}",
name,
if builtin { "(built-in)" } else { "(custom)" }
);
if !description.is_empty() {
println!(" {}", description);
}
if !tags.is_empty() {
println!(" Tags: {}", tags);
}
println!();
}
} else {
let error_text = response.text().await.unwrap_or_default();
eprintln!("\u{274c} Failed to list profiles: {}", error_text);
std::process::exit(1);
}
}
},
}
Ok(())
}