backtrace/bk/route_render.go
2026-08-07 10:19:13 +08:00

118 lines
3.2 KiB
Go

package backtrace
import (
"fmt"
"strings"
"github.com/oneclickvirt/backtrace/model"
. "github.com/oneclickvirt/defaultset"
)
// RenderRouteReport preserves the original compact one-target-per-line style.
// Detailed hop statistics remain available in RouteReport JSON instead of
// expanding the terminal section.
func RenderRouteReport(report RouteReport) string {
var builder strings.Builder
for _, target := range report.Targets {
var rendered string
switch {
case target.Status != RouteProbeAvailable:
rendered = Red("检测不到回程路由节点的IP地址")
default:
rendered = renderLegacyRouteLabels(target)
}
addressWidth := 15
if target.Target.IPVersion == "v6" {
addressWidth = 24
}
builder.WriteString(fmt.Sprintf("%v %-*s %v\n", target.Target.Name, addressWidth, target.Target.Address, rendered))
}
return strings.TrimSuffix(builder.String(), "\n")
}
// renderLegacyRouteLabels keeps the original terminal contract: every known
// carrier ASN observed in the trace remains visible. The conservative
// Classification is retained for JSON consumers, but its evidence-oriented
// fallback labels must not replace the classic human-readable route result.
func renderLegacyRouteLabels(target RouteTargetReport) string {
asns := uniqueStrings(target.ObservedASNs)
hasAS4134 := containsString(asns, "AS4134")
hasAS4809 := containsString(asns, "AS4809")
ordered := make([]string, 0, len(asns)+1)
if hasAS4809 {
if hasAS4134 {
ordered = append(ordered, "AS4809b")
} else {
ordered = append(ordered, "AS4809a")
}
}
ordered = append(ordered, asns...)
seenLabels := make(map[string]struct{})
labels := make([]string, 0, len(ordered))
for _, asn := range ordered {
if asn == "AS4809" {
continue
}
label := model.M[asn]
if label == "" {
continue
}
if _, exists := seenLabels[label]; exists {
continue
}
seenLabels[label] = struct{}{}
switch asn {
case "AS9929", "AS4809a", "AS23764":
labels = append(labels, DarkGreen(label))
case "AS4809b", "AS58807":
labels = append(labels, Green(label))
default:
labels = append(labels, White(label))
}
}
if len(labels) > 0 {
return strings.Join(labels, " ")
}
// Reports produced by older API clients may contain only Classification.
if target.Classification.Confidence != routeConfidenceInconclusive && target.Classification.Label != "" {
switch {
case target.Classification.Rank >= 4:
return DarkGreen(target.Classification.Label)
case target.Classification.Rank == 3:
return Green(target.Classification.Label)
default:
return White(target.Classification.Label)
}
}
return Red("检测不到已知线路的ASN")
}
func uniqueStrings(values []string) []string {
seen := make(map[string]struct{}, len(values))
result := make([]string, 0, len(values))
for _, value := range values {
value = strings.ToUpper(strings.TrimSpace(value))
if value == "" {
continue
}
if _, exists := seen[value]; exists {
continue
}
seen[value] = struct{}{}
result = append(result, value)
}
return result
}
func containsString(values []string, target string) bool {
for _, value := range values {
if value == target {
return true
}
}
return false
}