#!/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 [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 [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 # 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 [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 # 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 # Renders the full identity rule block in inspect. function ui::rule::identity_block() { local identity_name="${1:-}" strict="${2:-false}" local rules rules=$(identity::rules "$identity_name") [[ -z "$rules" ]] && return 0 printf "\n \033[0;37m· identity:%s\033[0m\n" "$identity_name" 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" done <<< "$rules" if [[ "$strict" == "true" ]]; then printf "\n \033[2m(strict — peer rule suppressed)\033[0m\n" fi } # ui::rule::_identity_rule_entry # Renders one rule within an identity block. function ui::rule::_identity_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 # Rule has extends — render one level deep using shared helper 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 # Leaf rule — show own entries or note full access 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 } # ====================================================== # 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 }