gistfile1.txt
· 12 KiB · Text
Raw
#!/usr/bin/env bash
# ui/rule.module.sh — rendering for rule data
# Replaces rule::render_* functions from rule.module.sh.
# All functions pure rendering — no writes, no state changes.
# ======================================================
# Entry Rendering (shared primitives)
# ======================================================
# ui::rule::entries <rule_name> [indent]
# Renders the fully resolved entries for a rule (allow + block).
function ui::rule::entries() {
local rule_name="${1:-}" indent="${2:-4}"
local allow_ports allow_ips block_ips block_ports dns
allow_ports=$(rule::get "$rule_name" "allow_ports" 2>/dev/null || true)
allow_ips=$(rule::get "$rule_name" "allow_ips" 2>/dev/null || true)
block_ips=$(rule::get "$rule_name" "block_ips" 2>/dev/null || true)
block_ports=$(rule::get "$rule_name" "block_ports" 2>/dev/null || true)
dns=$(rule::get_own "$rule_name" "dns_redirect")
while IFS= read -r e; do
[[ -z "$e" ]] && continue
net::print_entry "+" "$e" "$indent"
done <<< "$allow_ports"$'\n'"$allow_ips"
while IFS= read -r e; do
[[ -z "$e" ]] && continue
net::print_entry "-" "$e" "$indent"
done <<< "$block_ips"$'\n'"$block_ports"
[[ "${dns,,}" == "true" ]] && \
net::print_dns_redirect "$(config::dns)" 6 "DNS"
}
# ui::rule::own_entries <rule_name> [indent]
# Renders only the rule's own (non-inherited) entries.
function ui::rule::own_entries() {
local rule_name="${1:-}" indent="${2:-4}"
local rule_file
rule_file="$(rule::path "$rule_name")" || return 0
[[ -z "$rule_file" ]] && return 0
local allow_ports allow_ips block_ips block_ports dns
allow_ports=$(json::get "$rule_file" "allow_ports" 2>/dev/null || true)
allow_ips=$(json::get "$rule_file" "allow_ips" 2>/dev/null || true)
block_ips=$(json::get "$rule_file" "block_ips" 2>/dev/null || true)
block_ports=$(json::get "$rule_file" "block_ports" 2>/dev/null || true)
dns=$(json::get "$rule_file" "dns_redirect" 2>/dev/null || true)
local combined="${allow_ports}${allow_ips}${block_ips}${block_ports}"
[[ -z "${combined//[$'\n']/}" ]] && return 0
while IFS= read -r e; do
[[ -z "$e" ]] && continue
net::print_entry "+" "$e" "$indent"
done <<< "$allow_ports"$'\n'"$allow_ips"
while IFS= read -r e; do
[[ -z "$e" ]] && continue
net::print_entry "-" "$e" "$indent"
done <<< "$block_ips"$'\n'"$block_ports"
[[ "${dns,,}" == "true" ]] && \
net::print_dns_redirect "$(config::dns)" 6 "DNS"
}
# ui::rule::flat <rule_name>
# Renders the full resolved entries as a flat list.
function ui::rule::flat() {
local rule_name="${1:-}"
local allow_ports allow_ips block_ips block_ports dns
allow_ports=$(rule::get "$rule_name" "allow_ports")
allow_ips=$(rule::get "$rule_name" "allow_ips")
block_ips=$(rule::get "$rule_name" "block_ips")
block_ports=$(rule::get "$rule_name" "block_ports")
dns=$(rule::get_own "$rule_name" "dns_redirect")
local has_content=false
[[ -n "${allow_ports}${allow_ips}${block_ips}${block_ports}" ]] && has_content=true
if ! $has_content; then
printf "\n full access (no restrictions)\n"
return 0
fi
if [[ -n "$allow_ports" || -n "$allow_ips" ]]; then
printf "\n"
while IFS= read -r e; do
[[ -z "$e" ]] && continue
net::print_entry "+" "$e" 2
done <<< "$allow_ports"$'\n'"$allow_ips"
fi
if [[ -n "$block_ips" || -n "$block_ports" ]]; then
printf "\n"
while IFS= read -r e; do
[[ -z "$e" ]] && continue
net::print_entry "-" "$e" 2
done <<< "$block_ips"$'\n'"$block_ports"
fi
[[ "${dns,,}" == "true" ]] && \
net::print_dns_redirect "$(config::dns)" 6 "DNS"
}
# ======================================================
# Shared Base Renderer
# ======================================================
# ui::rule::_render_bases <array_nameref> [entry_indent] [label_indent]
# Renders a list of base rule names with newlines between them.
# Used by both ui::rule::tree and ui::rule::_identity_rule_entry.
function ui::rule::_render_bases() {
local -n _bases="$1"
local entry_indent="${2:-6}" label_indent="${3:-4}"
local label_pad
label_pad=$(printf '%*s' "$label_indent" '')
local first=true
for base_name in "${_bases[@]}"; do
[[ -z "$base_name" ]] && continue
$first || printf "\n"
first=false
printf "%s\033[0;37m↳ %s\033[0m\n" "$label_pad" "$base_name"
ui::rule::entries "$base_name" "$entry_indent"
done
}
# ======================================================
# Tree Rendering
# ======================================================
# ui::rule::tree <rule_name>
# Renders a rule's extends tree — one level deep with own entries.
# Returns 1 if rule has no extends (caller can fall back to flat).
function ui::rule::tree() {
local rule_name="${1:-}"
local rule_file
rule_file="$(rule::path "$rule_name")" || return 1
[[ -z "$rule_file" ]] && return 1
local extends_raw=()
mapfile -t extends_raw < <(json::get "$rule_file" "extends" 2>/dev/null || true) || true
if [[ ${#extends_raw[@]} -eq 0 || -z "${extends_raw[0]:-}" ]]; then
return 1
fi
ui::rule::_render_bases extends_raw 6 4
local own_output
own_output=$(ui::rule::own_entries "$rule_name" 6)
if [[ -n "$own_output" ]]; then
printf "\n \033[0;37mOwn:\033[0m\n"
printf "%s\n" "$own_output"
fi
return 0
}
# ======================================================
# Identity Rule Block
# ======================================================
# ui::rule::identity_block <identity_name> <strict_rule>
# Renders the full identity rule block in inspect.
function ui::rule::identity_block() {
local identity_name="${1:-}" strict="${2:-false}" no_header=false
# Parse optional flags
shift 2 || true
while [[ $# -gt 0 ]]; do
case "$1" in
--no-header) no_header=true; shift ;;
*) shift ;;
esac
done
local rules
rules=$(identity::rules "$identity_name")
[[ -z "$rules" ]] && return 0
if ! $no_header; then
printf "\n \033[0;37m· identity:%s\033[0m\n" "$identity_name"
fi
# Indentation levels:
# normal: entry=6 label=6 own=10 own_label=8
# no-header: entry=4 label=4 own=8 own_label=6
local entry_indent label_indent own_indent own_label_indent
if $no_header; then
entry_indent=4
label_indent=4
own_indent=8
own_label_indent=6
else
entry_indent=6
label_indent=6
own_indent=10
own_label_indent=8
fi
local first=true
while IFS= read -r rule_name; do
[[ -z "$rule_name" ]] && continue
$first || printf "\n"
first=false
ui::rule::_identity_rule_entry "$rule_name" \
"$entry_indent" "$label_indent" "$own_indent" "$own_label_indent"
done <<< "$rules"
if [[ "$strict" == "true" ]]; then
printf "\n \033[2m(strict — peer rule suppressed)\033[0m\n"
fi
}
# ui::rule::_identity_rule_entry <rule_name>
# Renders one rule within an identity block.
function ui::rule::_identity_rule_entry() {
local rule_name="${1:-}"
local entry_indent="${2:-6}"
local label_indent="${3:-6}"
local own_indent="${4:-10}"
local own_label_indent="${5:-8}"
local rule_file
rule_file="$(rule::path "$rule_name")" || return 0
local label_pad
label_pad=$(printf '%*s' "$label_indent" '')
printf "%s\033[0;37m↳ %s\033[0m\n" "$label_pad" "$rule_name"
local extends_raw=()
mapfile -t extends_raw < <(json::get "$rule_file" "extends" 2>/dev/null || true) || true
if [[ ${#extends_raw[@]} -gt 0 && -n "${extends_raw[0]:-}" ]]; then
ui::rule::_render_bases extends_raw "$own_indent" "$(( entry_indent + 2 ))"
local own_output
own_output=$(ui::rule::own_entries "$rule_name" "$own_indent")
if [[ -n "$own_output" ]]; then
local own_pad
own_pad=$(printf '%*s' "$(( entry_indent + 2 ))" '')
printf "\n%s\033[0;37mOwn:\033[0m\n" "$own_pad"
printf "%s\n" "$own_output"
fi
else
local own_output
own_output=$(ui::rule::own_entries "$rule_name" "$entry_indent")
if [[ -n "$own_output" ]]; then
printf "%s\n" "$own_output"
else
local full_pad
full_pad=$(printf '%*s' "$(( entry_indent + 2 ))" '')
printf "%s\033[2mfull access (no restrictions)\033[0m\n" "$full_pad"
fi
fi
}
# ======================================================
# Peer Entry
# ======================================================
function ui::rule::_peer_rule_entry() {
local rule_name="${1:-}"
local rule_file
rule_file="$(rule::path "$rule_name")" || return 0
printf " \033[0;37m↳ %s\033[0m\n" "$rule_name"
local extends_raw=()
mapfile -t extends_raw < <(json::get "$rule_file" "extends" 2>/dev/null || true) || true
if [[ ${#extends_raw[@]} -gt 0 && -n "${extends_raw[0]:-}" ]]; then
ui::rule::_render_bases extends_raw 10 8
local own_output
own_output=$(ui::rule::own_entries "$rule_name" 10)
if [[ -n "$own_output" ]]; then
printf "\n \033[0;37mOwn:\033[0m\n"
printf "%s\n" "$own_output"
fi
else
local own_output
own_output=$(ui::rule::own_entries "$rule_name" 8)
if [[ -n "$own_output" ]]; then
printf "%s\n" "$own_output"
else
printf " \033[2mfull access (no restrictions)\033[0m\n"
fi
fi
}
# ======================================================
# List Rendering
# ======================================================
# ui::rule::list_group_header <group_name>
function ui::rule::list_group_header() {
local group="${1:-}"
printf "\n \033[0;36m▸ %s\033[0m\n" "$group"
}
# ui::rule::list_base_header
function ui::rule::list_base_header() {
printf "\n \033[2m── Base Rules ──────────────────────\033[0m\n"
}
# ui::rule::list_row <name> <n_allows> <n_blocks> <peer_count> <w_name>
function ui::rule::list_row() {
local name="${1:-}" n_allows="${2:-0}" n_blocks="${3:-0}" \
peer_count="${4:-0}" w_name="${5:-16}" extends_csv="${6:-}"
local name_pad
name_pad=$(printf "%-${w_name}s" "$name")
local peer_word="peers"
[[ "$peer_count" -eq 1 ]] && peer_word="peer"
local peers_display
peers_display=$(printf "%s %s" "$peer_count" "$peer_word")
local peers_pad_n=$(( 10 - ${#peers_display} ))
[[ $peers_pad_n -lt 0 ]] && peers_pad_n=0
local extends_indicator=""
if [[ -n "$extends_csv" ]]; then
local extends_display="${extends_csv//,/, }"
extends_indicator=" \033[2m↳ ${extends_display}\033[0m"
fi
# Allows column — green +N if >0, dim +0 if zero
local allows_str
if [[ "$n_allows" -gt 0 ]]; then
allows_str=$(ui::pad_mb "\033[1;32m+${n_allows}\033[0m" 5)
else
allows_str=$(ui::pad_mb "\033[2m+0\033[0m" 5)
fi
# Blocks column — red -N if >0, dim -0 if zero
local blocks_str
if [[ "$n_blocks" -gt 0 ]]; then
blocks_str=$(ui::pad_mb "\033[1;31m-${n_blocks}\033[0m" 5)
else
blocks_str=$(ui::pad_mb "\033[2m-0\033[0m" 5)
fi
# Peers — dim if zero
local peers_colored
if [[ "$peer_count" -eq 0 ]]; then
peers_colored="\033[2m${peers_display}\033[0m"
else
peers_colored="$peers_display"
fi
printf " %s %b%b %b%*s%b\n" \
"$name_pad" "$allows_str" "$blocks_str" \
"$peers_colored" "$peers_pad_n" "" "$extends_indicator"
}
# ui::rule::list_extends <extends_csv>
# Renders the extends tree for a rule in list view (compact, one level)
function ui::rule::list_extends() {
local extends_csv="${1:-}"
[[ -z "$extends_csv" ]] && return 0
local extend_list=()
IFS=',' read -ra extend_list <<< "$extends_csv"
for base in "${extend_list[@]}"; do
[[ -z "$base" ]] && continue
printf " \033[0;37m ↳ %s\033[0m\n" "$base"
done
}
# ui::rule::list_extends_detailed <extends_csv> <rules_dir>
# Renders the extends tree with entries expanded (--detailed mode)
function ui::rule::list_extends_detailed() {
local extends_csv="${1:-}" rules_dir="${2:-}"
[[ -z "$extends_csv" ]] && return 0
local extend_list=()
IFS=',' read -ra extend_list <<< "$extends_csv"
for base in "${extend_list[@]}"; do
[[ -z "$base" ]] && continue
printf " \033[0;37m ↳ %s\033[0m\n" "$base"
ui::rule::entries "$base" 6
done
}
# ======================================================
# Show helpers
# ======================================================
function ui::rule::section_header() {
local title="${1:-}"
printf "\n \033[0;37m── %s ──────────────────────────────────\033[0m\n" "$title"
}
| 1 | #!/usr/bin/env bash |
| 2 | # ui/rule.module.sh — rendering for rule data |
| 3 | # Replaces rule::render_* functions from rule.module.sh. |
| 4 | # All functions pure rendering — no writes, no state changes. |
| 5 | |
| 6 | # ====================================================== |
| 7 | # Entry Rendering (shared primitives) |
| 8 | # ====================================================== |
| 9 | |
| 10 | # ui::rule::entries <rule_name> [indent] |
| 11 | # Renders the fully resolved entries for a rule (allow + block). |
| 12 | function ui::rule::entries() { |
| 13 | local rule_name="${1:-}" indent="${2:-4}" |
| 14 | |
| 15 | local allow_ports allow_ips block_ips block_ports dns |
| 16 | allow_ports=$(rule::get "$rule_name" "allow_ports" 2>/dev/null || true) |
| 17 | allow_ips=$(rule::get "$rule_name" "allow_ips" 2>/dev/null || true) |
| 18 | block_ips=$(rule::get "$rule_name" "block_ips" 2>/dev/null || true) |
| 19 | block_ports=$(rule::get "$rule_name" "block_ports" 2>/dev/null || true) |
| 20 | dns=$(rule::get_own "$rule_name" "dns_redirect") |
| 21 | |
| 22 | while IFS= read -r e; do |
| 23 | [[ -z "$e" ]] && continue |
| 24 | net::print_entry "+" "$e" "$indent" |
| 25 | done <<< "$allow_ports"$'\n'"$allow_ips" |
| 26 | |
| 27 | while IFS= read -r e; do |
| 28 | [[ -z "$e" ]] && continue |
| 29 | net::print_entry "-" "$e" "$indent" |
| 30 | done <<< "$block_ips"$'\n'"$block_ports" |
| 31 | |
| 32 | [[ "${dns,,}" == "true" ]] && \ |
| 33 | net::print_dns_redirect "$(config::dns)" 6 "DNS" |
| 34 | } |
| 35 | |
| 36 | # ui::rule::own_entries <rule_name> [indent] |
| 37 | # Renders only the rule's own (non-inherited) entries. |
| 38 | function ui::rule::own_entries() { |
| 39 | local rule_name="${1:-}" indent="${2:-4}" |
| 40 | local rule_file |
| 41 | rule_file="$(rule::path "$rule_name")" || return 0 |
| 42 | [[ -z "$rule_file" ]] && return 0 |
| 43 | |
| 44 | local allow_ports allow_ips block_ips block_ports dns |
| 45 | allow_ports=$(json::get "$rule_file" "allow_ports" 2>/dev/null || true) |
| 46 | allow_ips=$(json::get "$rule_file" "allow_ips" 2>/dev/null || true) |
| 47 | block_ips=$(json::get "$rule_file" "block_ips" 2>/dev/null || true) |
| 48 | block_ports=$(json::get "$rule_file" "block_ports" 2>/dev/null || true) |
| 49 | dns=$(json::get "$rule_file" "dns_redirect" 2>/dev/null || true) |
| 50 | |
| 51 | local combined="${allow_ports}${allow_ips}${block_ips}${block_ports}" |
| 52 | [[ -z "${combined//[$'\n']/}" ]] && return 0 |
| 53 | |
| 54 | while IFS= read -r e; do |
| 55 | [[ -z "$e" ]] && continue |
| 56 | net::print_entry "+" "$e" "$indent" |
| 57 | done <<< "$allow_ports"$'\n'"$allow_ips" |
| 58 | |
| 59 | while IFS= read -r e; do |
| 60 | [[ -z "$e" ]] && continue |
| 61 | net::print_entry "-" "$e" "$indent" |
| 62 | done <<< "$block_ips"$'\n'"$block_ports" |
| 63 | |
| 64 | [[ "${dns,,}" == "true" ]] && \ |
| 65 | net::print_dns_redirect "$(config::dns)" 6 "DNS" |
| 66 | } |
| 67 | |
| 68 | # ui::rule::flat <rule_name> |
| 69 | # Renders the full resolved entries as a flat list. |
| 70 | function ui::rule::flat() { |
| 71 | local rule_name="${1:-}" |
| 72 | |
| 73 | local allow_ports allow_ips block_ips block_ports dns |
| 74 | allow_ports=$(rule::get "$rule_name" "allow_ports") |
| 75 | allow_ips=$(rule::get "$rule_name" "allow_ips") |
| 76 | block_ips=$(rule::get "$rule_name" "block_ips") |
| 77 | block_ports=$(rule::get "$rule_name" "block_ports") |
| 78 | dns=$(rule::get_own "$rule_name" "dns_redirect") |
| 79 | |
| 80 | local has_content=false |
| 81 | [[ -n "${allow_ports}${allow_ips}${block_ips}${block_ports}" ]] && has_content=true |
| 82 | |
| 83 | if ! $has_content; then |
| 84 | printf "\n full access (no restrictions)\n" |
| 85 | return 0 |
| 86 | fi |
| 87 | |
| 88 | if [[ -n "$allow_ports" || -n "$allow_ips" ]]; then |
| 89 | printf "\n" |
| 90 | while IFS= read -r e; do |
| 91 | [[ -z "$e" ]] && continue |
| 92 | net::print_entry "+" "$e" 2 |
| 93 | done <<< "$allow_ports"$'\n'"$allow_ips" |
| 94 | fi |
| 95 | |
| 96 | if [[ -n "$block_ips" || -n "$block_ports" ]]; then |
| 97 | printf "\n" |
| 98 | while IFS= read -r e; do |
| 99 | [[ -z "$e" ]] && continue |
| 100 | net::print_entry "-" "$e" 2 |
| 101 | done <<< "$block_ips"$'\n'"$block_ports" |
| 102 | fi |
| 103 | |
| 104 | [[ "${dns,,}" == "true" ]] && \ |
| 105 | net::print_dns_redirect "$(config::dns)" 6 "DNS" |
| 106 | } |
| 107 | |
| 108 | # ====================================================== |
| 109 | # Shared Base Renderer |
| 110 | # ====================================================== |
| 111 | |
| 112 | # ui::rule::_render_bases <array_nameref> [entry_indent] [label_indent] |
| 113 | # Renders a list of base rule names with newlines between them. |
| 114 | # Used by both ui::rule::tree and ui::rule::_identity_rule_entry. |
| 115 | function ui::rule::_render_bases() { |
| 116 | local -n _bases="$1" |
| 117 | local entry_indent="${2:-6}" label_indent="${3:-4}" |
| 118 | local label_pad |
| 119 | label_pad=$(printf '%*s' "$label_indent" '') |
| 120 | |
| 121 | local first=true |
| 122 | for base_name in "${_bases[@]}"; do |
| 123 | [[ -z "$base_name" ]] && continue |
| 124 | $first || printf "\n" |
| 125 | first=false |
| 126 | printf "%s\033[0;37m↳ %s\033[0m\n" "$label_pad" "$base_name" |
| 127 | ui::rule::entries "$base_name" "$entry_indent" |
| 128 | done |
| 129 | } |
| 130 | |
| 131 | # ====================================================== |
| 132 | # Tree Rendering |
| 133 | # ====================================================== |
| 134 | |
| 135 | # ui::rule::tree <rule_name> |
| 136 | # Renders a rule's extends tree — one level deep with own entries. |
| 137 | # Returns 1 if rule has no extends (caller can fall back to flat). |
| 138 | function ui::rule::tree() { |
| 139 | local rule_name="${1:-}" |
| 140 | local rule_file |
| 141 | rule_file="$(rule::path "$rule_name")" || return 1 |
| 142 | [[ -z "$rule_file" ]] && return 1 |
| 143 | |
| 144 | local extends_raw=() |
| 145 | mapfile -t extends_raw < <(json::get "$rule_file" "extends" 2>/dev/null || true) || true |
| 146 | |
| 147 | if [[ ${#extends_raw[@]} -eq 0 || -z "${extends_raw[0]:-}" ]]; then |
| 148 | return 1 |
| 149 | fi |
| 150 | |
| 151 | ui::rule::_render_bases extends_raw 6 4 |
| 152 | |
| 153 | local own_output |
| 154 | own_output=$(ui::rule::own_entries "$rule_name" 6) |
| 155 | if [[ -n "$own_output" ]]; then |
| 156 | printf "\n \033[0;37mOwn:\033[0m\n" |
| 157 | printf "%s\n" "$own_output" |
| 158 | fi |
| 159 | |
| 160 | return 0 |
| 161 | } |
| 162 | |
| 163 | # ====================================================== |
| 164 | # Identity Rule Block |
| 165 | # ====================================================== |
| 166 | |
| 167 | # ui::rule::identity_block <identity_name> <strict_rule> |
| 168 | # Renders the full identity rule block in inspect. |
| 169 | function ui::rule::identity_block() { |
| 170 | local identity_name="${1:-}" strict="${2:-false}" no_header=false |
| 171 | |
| 172 | # Parse optional flags |
| 173 | shift 2 || true |
| 174 | while [[ $# -gt 0 ]]; do |
| 175 | case "$1" in |
| 176 | --no-header) no_header=true; shift ;; |
| 177 | *) shift ;; |
| 178 | esac |
| 179 | done |
| 180 | |
| 181 | local rules |
| 182 | rules=$(identity::rules "$identity_name") |
| 183 | [[ -z "$rules" ]] && return 0 |
| 184 | |
| 185 | if ! $no_header; then |
| 186 | printf "\n \033[0;37m· identity:%s\033[0m\n" "$identity_name" |
| 187 | fi |
| 188 | |
| 189 | # Indentation levels: |
| 190 | # normal: entry=6 label=6 own=10 own_label=8 |
| 191 | # no-header: entry=4 label=4 own=8 own_label=6 |
| 192 | local entry_indent label_indent own_indent own_label_indent |
| 193 | if $no_header; then |
| 194 | entry_indent=4 |
| 195 | label_indent=4 |
| 196 | own_indent=8 |
| 197 | own_label_indent=6 |
| 198 | else |
| 199 | entry_indent=6 |
| 200 | label_indent=6 |
| 201 | own_indent=10 |
| 202 | own_label_indent=8 |
| 203 | fi |
| 204 | |
| 205 | local first=true |
| 206 | while IFS= read -r rule_name; do |
| 207 | [[ -z "$rule_name" ]] && continue |
| 208 | $first || printf "\n" |
| 209 | first=false |
| 210 | ui::rule::_identity_rule_entry "$rule_name" \ |
| 211 | "$entry_indent" "$label_indent" "$own_indent" "$own_label_indent" |
| 212 | done <<< "$rules" |
| 213 | |
| 214 | if [[ "$strict" == "true" ]]; then |
| 215 | printf "\n \033[2m(strict — peer rule suppressed)\033[0m\n" |
| 216 | fi |
| 217 | } |
| 218 | |
| 219 | # ui::rule::_identity_rule_entry <rule_name> |
| 220 | # Renders one rule within an identity block. |
| 221 | function ui::rule::_identity_rule_entry() { |
| 222 | local rule_name="${1:-}" |
| 223 | local entry_indent="${2:-6}" |
| 224 | local label_indent="${3:-6}" |
| 225 | local own_indent="${4:-10}" |
| 226 | local own_label_indent="${5:-8}" |
| 227 | |
| 228 | local rule_file |
| 229 | rule_file="$(rule::path "$rule_name")" || return 0 |
| 230 | |
| 231 | local label_pad |
| 232 | label_pad=$(printf '%*s' "$label_indent" '') |
| 233 | printf "%s\033[0;37m↳ %s\033[0m\n" "$label_pad" "$rule_name" |
| 234 | |
| 235 | local extends_raw=() |
| 236 | mapfile -t extends_raw < <(json::get "$rule_file" "extends" 2>/dev/null || true) || true |
| 237 | |
| 238 | if [[ ${#extends_raw[@]} -gt 0 && -n "${extends_raw[0]:-}" ]]; then |
| 239 | ui::rule::_render_bases extends_raw "$own_indent" "$(( entry_indent + 2 ))" |
| 240 | |
| 241 | local own_output |
| 242 | own_output=$(ui::rule::own_entries "$rule_name" "$own_indent") |
| 243 | if [[ -n "$own_output" ]]; then |
| 244 | local own_pad |
| 245 | own_pad=$(printf '%*s' "$(( entry_indent + 2 ))" '') |
| 246 | printf "\n%s\033[0;37mOwn:\033[0m\n" "$own_pad" |
| 247 | printf "%s\n" "$own_output" |
| 248 | fi |
| 249 | else |
| 250 | local own_output |
| 251 | own_output=$(ui::rule::own_entries "$rule_name" "$entry_indent") |
| 252 | if [[ -n "$own_output" ]]; then |
| 253 | printf "%s\n" "$own_output" |
| 254 | else |
| 255 | local full_pad |
| 256 | full_pad=$(printf '%*s' "$(( entry_indent + 2 ))" '') |
| 257 | printf "%s\033[2mfull access (no restrictions)\033[0m\n" "$full_pad" |
| 258 | fi |
| 259 | fi |
| 260 | } |
| 261 | |
| 262 | # ====================================================== |
| 263 | # Peer Entry |
| 264 | # ====================================================== |
| 265 | |
| 266 | function ui::rule::_peer_rule_entry() { |
| 267 | local rule_name="${1:-}" |
| 268 | local rule_file |
| 269 | rule_file="$(rule::path "$rule_name")" || return 0 |
| 270 | |
| 271 | printf " \033[0;37m↳ %s\033[0m\n" "$rule_name" |
| 272 | |
| 273 | local extends_raw=() |
| 274 | mapfile -t extends_raw < <(json::get "$rule_file" "extends" 2>/dev/null || true) || true |
| 275 | |
| 276 | if [[ ${#extends_raw[@]} -gt 0 && -n "${extends_raw[0]:-}" ]]; then |
| 277 | ui::rule::_render_bases extends_raw 10 8 |
| 278 | |
| 279 | local own_output |
| 280 | own_output=$(ui::rule::own_entries "$rule_name" 10) |
| 281 | if [[ -n "$own_output" ]]; then |
| 282 | printf "\n \033[0;37mOwn:\033[0m\n" |
| 283 | printf "%s\n" "$own_output" |
| 284 | fi |
| 285 | else |
| 286 | local own_output |
| 287 | own_output=$(ui::rule::own_entries "$rule_name" 8) |
| 288 | if [[ -n "$own_output" ]]; then |
| 289 | printf "%s\n" "$own_output" |
| 290 | else |
| 291 | printf " \033[2mfull access (no restrictions)\033[0m\n" |
| 292 | fi |
| 293 | fi |
| 294 | } |
| 295 | |
| 296 | # ====================================================== |
| 297 | # List Rendering |
| 298 | # ====================================================== |
| 299 | |
| 300 | # ui::rule::list_group_header <group_name> |
| 301 | function ui::rule::list_group_header() { |
| 302 | local group="${1:-}" |
| 303 | printf "\n \033[0;36m▸ %s\033[0m\n" "$group" |
| 304 | } |
| 305 | |
| 306 | # ui::rule::list_base_header |
| 307 | function ui::rule::list_base_header() { |
| 308 | printf "\n \033[2m── Base Rules ──────────────────────\033[0m\n" |
| 309 | } |
| 310 | |
| 311 | |
| 312 | # ui::rule::list_row <name> <n_allows> <n_blocks> <peer_count> <w_name> |
| 313 | function ui::rule::list_row() { |
| 314 | local name="${1:-}" n_allows="${2:-0}" n_blocks="${3:-0}" \ |
| 315 | peer_count="${4:-0}" w_name="${5:-16}" extends_csv="${6:-}" |
| 316 | |
| 317 | local name_pad |
| 318 | name_pad=$(printf "%-${w_name}s" "$name") |
| 319 | |
| 320 | local peer_word="peers" |
| 321 | [[ "$peer_count" -eq 1 ]] && peer_word="peer" |
| 322 | |
| 323 | local peers_display |
| 324 | peers_display=$(printf "%s %s" "$peer_count" "$peer_word") |
| 325 | local peers_pad_n=$(( 10 - ${#peers_display} )) |
| 326 | [[ $peers_pad_n -lt 0 ]] && peers_pad_n=0 |
| 327 | |
| 328 | local extends_indicator="" |
| 329 | if [[ -n "$extends_csv" ]]; then |
| 330 | local extends_display="${extends_csv//,/, }" |
| 331 | extends_indicator=" \033[2m↳ ${extends_display}\033[0m" |
| 332 | fi |
| 333 | |
| 334 | # Allows column — green +N if >0, dim +0 if zero |
| 335 | local allows_str |
| 336 | if [[ "$n_allows" -gt 0 ]]; then |
| 337 | allows_str=$(ui::pad_mb "\033[1;32m+${n_allows}\033[0m" 5) |
| 338 | else |
| 339 | allows_str=$(ui::pad_mb "\033[2m+0\033[0m" 5) |
| 340 | fi |
| 341 | |
| 342 | # Blocks column — red -N if >0, dim -0 if zero |
| 343 | local blocks_str |
| 344 | if [[ "$n_blocks" -gt 0 ]]; then |
| 345 | blocks_str=$(ui::pad_mb "\033[1;31m-${n_blocks}\033[0m" 5) |
| 346 | else |
| 347 | blocks_str=$(ui::pad_mb "\033[2m-0\033[0m" 5) |
| 348 | fi |
| 349 | |
| 350 | # Peers — dim if zero |
| 351 | local peers_colored |
| 352 | if [[ "$peer_count" -eq 0 ]]; then |
| 353 | peers_colored="\033[2m${peers_display}\033[0m" |
| 354 | else |
| 355 | peers_colored="$peers_display" |
| 356 | fi |
| 357 | |
| 358 | printf " %s %b%b %b%*s%b\n" \ |
| 359 | "$name_pad" "$allows_str" "$blocks_str" \ |
| 360 | "$peers_colored" "$peers_pad_n" "" "$extends_indicator" |
| 361 | } |
| 362 | |
| 363 | # ui::rule::list_extends <extends_csv> |
| 364 | # Renders the extends tree for a rule in list view (compact, one level) |
| 365 | function ui::rule::list_extends() { |
| 366 | local extends_csv="${1:-}" |
| 367 | [[ -z "$extends_csv" ]] && return 0 |
| 368 | |
| 369 | local extend_list=() |
| 370 | IFS=',' read -ra extend_list <<< "$extends_csv" |
| 371 | for base in "${extend_list[@]}"; do |
| 372 | [[ -z "$base" ]] && continue |
| 373 | printf " \033[0;37m ↳ %s\033[0m\n" "$base" |
| 374 | done |
| 375 | } |
| 376 | |
| 377 | # ui::rule::list_extends_detailed <extends_csv> <rules_dir> |
| 378 | # Renders the extends tree with entries expanded (--detailed mode) |
| 379 | function ui::rule::list_extends_detailed() { |
| 380 | local extends_csv="${1:-}" rules_dir="${2:-}" |
| 381 | [[ -z "$extends_csv" ]] && return 0 |
| 382 | |
| 383 | local extend_list=() |
| 384 | IFS=',' read -ra extend_list <<< "$extends_csv" |
| 385 | for base in "${extend_list[@]}"; do |
| 386 | [[ -z "$base" ]] && continue |
| 387 | printf " \033[0;37m ↳ %s\033[0m\n" "$base" |
| 388 | ui::rule::entries "$base" 6 |
| 389 | done |
| 390 | } |
| 391 | |
| 392 | # ====================================================== |
| 393 | # Show helpers |
| 394 | # ====================================================== |
| 395 | |
| 396 | function ui::rule::section_header() { |
| 397 | local title="${1:-}" |
| 398 | printf "\n \033[0;37m── %s ──────────────────────────────────\033[0m\n" "$title" |
| 399 | } |