#!/usr/bin/env bash # ui/subnet.module.sh — rendering for subnet data # All functions pure rendering — no writes, no state changes. function ui::subnet::header() { printf " %-14s %-18s %-10s %-8s %s\n" \ "NAME" "SUBNET" "TYPE" "TUNNEL" "DESCRIPTION" ui::divider 70 } function ui::subnet::row() { local display_name="${1:-}" subnet="${2:-}" type_key="${3:-}" \ tunnel_mode="${4:-}" desc="${5:-}" is_group="${6:-false}" local name_col="$display_name" [[ "$is_group" == "true" ]] && name_col=" ${display_name}" printf " %-14s %-18s %-10s %-8s %s\n" \ "$name_col" "$subnet" "$type_key" "$tunnel_mode" "$desc" } function ui::subnet::row_scalar() { local name="${1:-}" subnet="${2:-}" tunnel="${3:-split}" local tunnel_val tunnel_val=$(printf "%-8s" "$tunnel") printf " %-14s %-18s \033[2mtunnel:\033[0m %s\n" \ "$name" "$subnet" "$tunnel_val" } function ui::subnet::row_group_parent() { local name="${1:-}" printf " \033[1m%s\033[0m\n" "$name" } function ui::subnet::row_group_child() { local type_key="${1:-}" subnet="${2:-}" tunnel="${3:-split}" local tunnel_val tunnel_val=$(printf "%-8s" "$tunnel") printf " · %-10s %-18s \033[2mtunnel:\033[0m %s\n" \ "$type_key" "$subnet" "$tunnel_val" } function ui::subnet::group_separator() { echo "" } function ui::subnet::detail() { local name="${1:-}" is_group="${2:-false}" ui::row "Name" "$name" ui::row "Type" "$( [[ "$is_group" == "true" ]] && echo "group" || echo "scalar" )" } function ui::subnet::detail_field() { local key="${1:-}" value="${2:-}" ui::row "$key" "$value" } function ui::subnet::child_header() { printf "\n" printf " %-12s %-18s %-8s %s\n" "TYPE" "SUBNET" "TUNNEL" "DESCRIPTION" ui::divider 56 } function ui::subnet::child_row() { local type_key="${1:-}" subnet="${2:-}" tunnel_mode="${3:-}" desc="${4:-}" printf " %-12s %-18s %-8s %s\n" "$type_key" "$subnet" "$tunnel_mode" "$desc" } function ui::subnet::peers_in_use() { local peers_csv="${1:-}" [[ -z "$peers_csv" ]] && return 0 echo "" ui::row "Peers using" "${peers_csv//,/, }" } function ui::subnet::show_scalar() { local name="${1:-}" subnet="${2:-}" tunnel="${3:-}" desc="${4:-}" echo "" printf " \033[1m%-16s\033[0m %-18s \033[2mtunnel:\033[0m %s\n" \ "$name" "$subnet" "$tunnel" echo "" [[ -n "$desc" ]] && printf " \033[2mDescription:\033[0m %s\n" "$desc" } function ui::subnet::show_group() { local name="${1:-}" echo "" printf " \033[1m%s\033[0m\n" "$name" echo "" } function ui::subnet::show_child_row() { local type_key="${1:-}" subnet="${2:-}" tunnel="${3:-}" desc="${4:-}" local desc_part="" [[ -n "$desc" ]] && desc_part=" $desc" printf " · %-10s %-18s \033[2mtunnel:\033[0m %-8s%s\n" \ "$type_key" "$subnet" "$tunnel" "$desc_part" } function ui::subnet::show_peers() { local peers_csv="${1:-}" [[ -z "$peers_csv" ]] && return 0 echo "" local peers_arr=() IFS=',' read -ra peers_arr <<< "$peers_csv" local count="${#peers_arr[@]}" printf " Peers (%s):\n" "$count" # Group peers by identity, then by subnet child (for groups) # Build: identity -> list of peer names declare -A identity_peers_map=() local no_identity=() for peer in "${peers_arr[@]}"; do peer="${peer// /}" [[ -z "$peer" ]] && continue local id_name id_name=$(identity::get_name "$peer") if [[ -n "$id_name" ]]; then identity_peers_map["$id_name"]+="${peer}," else no_identity+=("$peer") fi done # Print identity groups on same line for id_name in "${!identity_peers_map[@]}"; do local peer_list="${identity_peers_map[$id_name]%,}" printf " · %s\n" "${peer_list//,/, }" done # Print peers without identity one per line for peer in "${no_identity[@]}"; do printf " · %s\n" "$peer" done } function ui::subnet::show_peers_annotated() { # For group subnets — peers annotated with their subnet child local peers_csv="${1:-}" subnets_file="${2:-}" [[ -z "$peers_csv" ]] && return 0 local peers_arr=() IFS=',' read -ra peers_arr <<< "$peers_csv" local count="${#peers_arr[@]}" echo "" printf " Peers (%s):\n" "$count" declare -A identity_peers_map=() # identity -> "peer→child,peer→child" local no_identity=() for peer in "${peers_arr[@]}"; do peer="${peer// /}" [[ -z "$peer" ]] && continue # Find which child subnet this peer's IP belongs to local peer_ip child_name="" peer_ip=$(peers::get_ip "$peer" 2>/dev/null) || peer_ip="" if [[ -n "$peer_ip" ]]; then local result result=$(json::subnet_for_ip "$subnets_file" "$peer_ip" 2>/dev/null) || true [[ -n "$result" ]] && child_name="${result##*|}" fi local annotated="${peer}" [[ -n "$child_name" ]] && annotated="${peer} \033[2m→ ${child_name}\033[0m" local id_name id_name=$(identity::get_name "$peer") if [[ -n "$id_name" ]]; then identity_peers_map["$id_name"]+="${annotated}," else no_identity+=("$annotated") fi done for id_name in "${!identity_peers_map[@]}"; do local peer_list="${identity_peers_map[$id_name]%,}" printf " · %b\n" "${peer_list//,/, }" done for peer in "${no_identity[@]}"; do printf " · %b\n" "$peer" done } # ====================================================== # Table view # ====================================================== function ui::subnet::list_header_table() { printf "\n %-14s %-20s %-8s %s\n" \ "TYPE" "CIDR" "TUNNEL" "DESCRIPTION" printf " %s\n" "$(printf '─%.0s' {1..65})" } function ui::subnet::list_row_table() { local type="${1:-}" cidr="${2:-}" tunnel="${3:-}" desc="${4:-}" printf " %-14s %-20s %-8s %s\n" \ "$type" "$cidr" "$tunnel" "${desc:--}" }