seamless-glance 1.0.0

Fast, read-only AWS TUI for cloud infrastructure visibility and triage
use crate::aws::clients::AwsClients;
use crate::models::describable::{shell_quote, DescribableResource};
use anyhow::Result;
use async_trait::async_trait;

#[derive(Debug, Clone)]
pub struct TargetGroupInfo {
    pub arn: String,
    pub name: String,
    pub protocol: String,
    pub port: i32,
    pub target_type: String,
    pub attached_load_balancer_arns: Vec<String>,
    pub total_targets: usize,
    pub unhealthy_targets: usize,
}

impl TargetGroupInfo {
    pub fn healthy_targets(&self) -> usize {
        self.total_targets.saturating_sub(self.unhealthy_targets)
    }

    pub fn has_zero_healthy_targets(&self) -> bool {
        self.total_targets > 0 && self.healthy_targets() == 0
    }

    pub fn attached_load_balancer_count(&self) -> usize {
        self.attached_load_balancer_arns.len()
    }

    pub fn has_load_balancer_attachment(&self) -> bool {
        !self.attached_load_balancer_arns.is_empty()
    }

    pub fn is_orphan_candidate(&self) -> bool {
        !self.has_load_balancer_attachment() && self.total_targets == 0
    }

    pub fn review_signals(&self) -> Vec<&'static str> {
        let mut signals = Vec::new();

        if self.is_orphan_candidate() {
            signals.push("orphan");
        }

        if self.has_zero_healthy_targets() {
            signals.push("zero-healthy");
        } else if self.unhealthy_targets > 0 {
            signals.push("unhealthy");
        }

        signals
    }
}

#[async_trait]
impl DescribableResource for TargetGroupInfo {
    fn resource_name(&self) -> String {
        self.name.clone()
    }

    async fn describe(&self, clients: &AwsClients) -> Result<String> {
        let tg = clients
            .elb
            .describe_target_groups()
            .target_group_arns(&self.arn)
            .send()
            .await?;

        let health = clients
            .elb
            .describe_target_health()
            .target_group_arn(&self.arn)
            .send()
            .await?;

        Ok(format!(
            "Target Group:\n{:#?}\n\nTarget Health:\n{:#?}",
            tg, health
        ))
    }

    fn console_url(&self, region: &str) -> Option<String> {
        Some(format!(
            "https://console.aws.amazon.com/ec2/v2/home?region={region}#TargetGroup:targetGroupArn={}",
            self.arn
        ))
    }

    fn cli_command(&self, region: &str) -> Option<String> {
        Some(format!(
            "aws elbv2 describe-target-health --target-group-arn {} --region {}",
            shell_quote(&self.arn),
            shell_quote(region)
        ))
    }
}