use clap::{Args, Subcommand};
use homeboy::server::{self, Server};
use homeboy::shell;
use homeboy::ssh::{resolve_context, SshClient, SshResolveArgs};
use serde::Serialize;
use super::CmdResult;
#[derive(Args)]
pub struct SshArgs {
#[arg(conflicts_with_all = ["project", "server"])]
pub id: Option<String>,
#[arg(long, conflicts_with_all = ["server", "id"])]
pub project: Option<String>,
#[arg(long, conflicts_with_all = ["project", "id"])]
pub server: Option<String>,
pub command: Option<String>,
#[command(subcommand)]
pub subcommand: Option<SshSubcommand>,
}
#[derive(Subcommand)]
pub enum SshSubcommand {
List,
}
#[derive(Debug, Serialize)]
#[serde(tag = "action")]
pub enum SshOutput {
Connect(SshConnectOutput),
List(SshListOutput),
}
#[derive(Debug, Serialize)]
pub struct SshConnectOutput {
pub resolved_type: String,
pub project_id: Option<String>,
pub server_id: String,
pub command: Option<String>,
}
#[derive(Debug, Serialize)]
pub struct SshListOutput {
pub servers: Vec<Server>,
}
pub fn run(args: SshArgs, _global: &crate::commands::GlobalArgs) -> CmdResult<SshOutput> {
match args.subcommand {
Some(SshSubcommand::List) => {
let servers = server::list()?;
Ok((SshOutput::List(SshListOutput { servers }), 0))
}
None => {
let resolve_args = SshResolveArgs {
id: args.id.clone(),
project: args.project.clone(),
server: args.server.clone(),
};
let result = resolve_context(&resolve_args)?;
let effective_command = match (&result.project_id, &result.base_path, &args.command) {
(Some(_), Some(bp), Some(cmd)) => {
Some(format!("cd {} && {}", shell::quote_path(bp), cmd))
}
(Some(_), Some(bp), None) => Some(format!("cd {}", shell::quote_path(bp))),
_ => args.command.clone(),
};
let client = SshClient::from_server(&result.server, &result.server_id)?;
let exit_code = client.execute_interactive(effective_command.as_deref());
Ok((
SshOutput::Connect(SshConnectOutput {
resolved_type: result.resolved_type,
project_id: result.project_id,
server_id: result.server_id,
command: args.command,
}),
exit_code,
))
}
}
}