use clap::{Parser, Subcommand};
use ffs_cli::database::Database;
use ffs_cli::providers::ProviderFactory;
use ffs_cli::utils::timestamp;
const DEFAULT_PROVIDER: &str = "hetzner";
const DEFAULT_JOB_NAME_PREFIX: &str = "ffs-job-";
#[derive(Parser)]
#[command(name = "ffs")]
#[command(about = "A CLI tool for managing cloud computing jobs")]
#[command(version = "0.1.1")]
struct Cli {
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand)]
enum Commands {
Init {
#[arg(default_value = DEFAULT_PROVIDER)]
provider: String,
},
#[command(alias = "ls")]
List,
Start {
name: Option<String>,
},
Stop {
id: String,
},
Tail {
id: String,
filename: String,
},
Scp {
id: String,
filename: String,
destination: String,
},
Ssh {
id: String,
},
Version,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let cli = Cli::parse();
let mut database = Database::new();
let provider = ProviderFactory::create_provider(
database
.get("provider")
.unwrap_or_else(|| DEFAULT_PROVIDER.to_string())
.as_str(),
);
match cli.command.unwrap_or(Commands::List) {
Commands::Init {
provider: provider_name,
} => {
database.set("provider", &provider_name)?;
println!("Provider set to: {provider_name}");
}
Commands::List => {
if let Ok(jobs) = provider.list_jobs().await {
for job in jobs {
println!("{job:?}");
}
}
}
Commands::Start { name } => {
let job_name =
name.unwrap_or_else(|| format!("{DEFAULT_JOB_NAME_PREFIX}{}", timestamp()));
println!("Starting job {job_name}");
let job = provider.start_job(&job_name).await?;
println!("Job {job:?} started");
}
Commands::Stop { id } => {
println!("Stopping job {id}");
let job = provider.stop_job(&id).await?;
println!("Job {job:?} stopped");
}
Commands::Tail { id, filename } => {
println!("Fetching logs for job {id} at {filename}");
provider.tail(&id, &filename).await?;
}
Commands::Scp {
id,
filename,
destination,
} => {
println!("Copying {filename} to {destination} for job {id}");
provider.scp(&id, &filename, &destination).await?;
}
Commands::Ssh { id } => match provider.get_job(&id).await? {
Some(job) => {
println!("Connecting to {}", job.ipv4);
std::process::Command::new("ssh")
.arg(format!("root@{}", job.ipv4))
.status()?;
}
None => println!("Job {id} not found"),
},
Commands::Version => {
println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
}
}
Ok(())
}