feat: implement output formatting and display (HAN-383)
CI / test (pull_request) Failing after 2m2s

Add text and JSON formatters with real-time progress via traverser hooks,
summary statistics, and CLI integration for --show-* and --json flags.

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
Gary Hansen
2026-06-08 02:21:13 +10:00
co-authored by multica-agent
parent 876542b2de
commit 5a54834c5f
13 changed files with 1359 additions and 180 deletions
+27 -1
View File
@@ -16,6 +16,7 @@ type TraverserConfig struct {
RootConfig *dns.RootDiscoveryConfig
QueryConfig *dns.QueryConfig
RootAddrs []net.IP
Hooks *TraverserHooks
}
func DefaultTraverserConfig() *TraverserConfig {
@@ -57,6 +58,13 @@ func (t *Traverser) SetExchange(fn dns.ExchangeFunc) {
t.exchange = fn
}
func (t *Traverser) SetHooks(hooks *TraverserHooks) {
if t.config == nil {
t.config = DefaultTraverserConfig()
}
t.config.Hooks = hooks
}
func (t *Traverser) Traverse(ctx context.Context, name string) ([]TraversalResult, error) {
name = miekgdns.Fqdn(name)
@@ -94,9 +102,19 @@ func (t *Traverser) Traverse(ctx context.Context, name string) ([]TraversalResul
cache = rootCache.Child()
}
if t.config.Hooks != nil {
t.config.Hooks.emit(EventStart, TraversalResult{Referral: ref}, false)
}
resp := t.processReferral(ctx, ref, cache)
result := TraversalResult{Referral: ref, Response: resp}
if t.config.Hooks != nil {
t.config.Hooks.emit(EventComplete, result, false)
}
mu.Lock()
results = append(results, TraversalResult{Referral: ref, Response: resp})
results = append(results, result)
mu.Unlock()
if resp.IsTerminal() {
@@ -266,8 +284,16 @@ func (t *Traverser) ResolveNS(ctx context.Context, nsName string, cache *InfoCac
cacheForStep = traversalCache.Child()
}
if t.config.Hooks != nil {
t.config.Hooks.emit(EventStart, TraversalResult{Referral: current}, true)
}
resp := t.processReferral(ctx, current, cacheForStep)
if t.config.Hooks != nil {
t.config.Hooks.emit(EventComplete, TraversalResult{Referral: current, Response: resp}, true)
}
if resp.Type == RespAnswer && len(resp.Decoded.Answers) > 0 {
for _, rr := range resp.Decoded.Answers {
if a, ok := rr.(*miekgdns.A); ok {