From 344a6f6415c3c1b593677adec3b8844e0839971b Mon Sep 17 00:00:00 2001 From: s Date: Thu, 13 Nov 2025 14:43:15 -0500 Subject: created pretty printing for all commands --- internal/formatter/npd.go | 139 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 internal/formatter/npd.go (limited to 'internal/formatter/npd.go') diff --git a/internal/formatter/npd.go b/internal/formatter/npd.go new file mode 100644 index 0000000..0d15d7b --- /dev/null +++ b/internal/formatter/npd.go @@ -0,0 +1,139 @@ +package formatter + +import ( + "encoding/json" + "fmt" + "strings" + + "git.db.org.ai/dborg/internal/models" +) + +func FormatNPDResults(resp *models.NPDResponse, asJSON bool) (string, error) { + if asJSON { + data, err := json.MarshalIndent(resp.Results.Hits, "", " ") + if err != nil { + return "", fmt.Errorf("failed to marshal JSON: %w", err) + } + return string(data), nil + } + + var sb strings.Builder + + sb.WriteString(fmt.Sprintf("\n%s\n", Bold(Cyan("NPD Breach Data Search Results")))) + sb.WriteString(fmt.Sprintf("%s\n\n", Gray(strings.Repeat("─", 50)))) + + sb.WriteString(fmt.Sprintf("%s %s / %s max\n", + Blue("Total Hits:"), + Yellow(fmt.Sprintf("%d", resp.Results.NumHits)), + Dim(fmt.Sprintf("%d", resp.MaxHits)))) + sb.WriteString(fmt.Sprintf("%s %s\n\n", + Blue("Query Time:"), + Dim(fmt.Sprintf("%d microseconds", resp.Results.ElapsedTimeMicros)))) + + if len(resp.Results.Errors) > 0 { + sb.WriteString(fmt.Sprintf("%s\n", Red("Errors:"))) + for _, err := range resp.Results.Errors { + sb.WriteString(fmt.Sprintf(" %s %s\n", Red("✗"), err)) + } + sb.WriteString("\n") + } + + if len(resp.Results.Hits) > 0 { + sb.WriteString(fmt.Sprintf("%s\n", Bold("Records:"))) + sb.WriteString(fmt.Sprintf("%s\n", Gray(strings.Repeat("─", 50)))) + + for i, hit := range resp.Results.Hits { + sb.WriteString(fmt.Sprintf("\n%s:\n", Bold(Blue(fmt.Sprintf("Record %d", i+1))))) + + if name := getStringField(hit, "firstname", "lastname", "middlename"); name != "" { + sb.WriteString(fmt.Sprintf(" %s %s\n", Cyan("Name:"), Bold(name))) + } + + if dob := getStringField(hit, "dob"); dob != "" { + sb.WriteString(fmt.Sprintf(" %s %s\n", Cyan("DOB:"), dob)) + } + + if ssn := getStringField(hit, "ssn"); ssn != "" { + sb.WriteString(fmt.Sprintf(" %s %s\n", Cyan("SSN:"), Yellow(ssn))) + } + + if phone := getStringField(hit, "phone1"); phone != "" { + sb.WriteString(fmt.Sprintf(" %s %s\n", Cyan("Phone:"), phone)) + } + + if addr := buildAddress(hit); addr != "" { + sb.WriteString(fmt.Sprintf(" %s %s\n", Cyan("Address:"), addr)) + } + + for k, v := range hit { + if !isCommonField(k) && v != nil && v != "" { + sb.WriteString(fmt.Sprintf(" %s %v\n", Dim(fmt.Sprintf("%s:", k)), v)) + } + } + } + } else { + sb.WriteString(fmt.Sprintf("%s\n", Gray("No records found"))) + } + + sb.WriteString("\n") + if resp.Credits.Unlimited { + sb.WriteString(fmt.Sprintf("%s: %s\n", Dim("Credits"), Green("Unlimited"))) + } else { + sb.WriteString(fmt.Sprintf("%s: %s\n", Dim("Credits Remaining"), FormatCredits(int64(resp.Credits.Remaining)))) + } + + return sb.String(), nil +} + +func getStringField(data map[string]any, keys ...string) string { + var parts []string + for _, key := range keys { + if val, ok := data[key]; ok && val != nil { + if str, ok := val.(string); ok && str != "" { + parts = append(parts, str) + } + } + } + return strings.Join(parts, " ") +} + +func buildAddress(data map[string]any) string { + var parts []string + + if addr, ok := data["address"].(string); ok && addr != "" { + parts = append(parts, addr) + } + + cityStateZip := []string{} + if city, ok := data["city"].(string); ok && city != "" { + cityStateZip = append(cityStateZip, city) + } + if state, ok := data["st"].(string); ok && state != "" { + cityStateZip = append(cityStateZip, state) + } + if zip, ok := data["zip"].(string); ok && zip != "" { + cityStateZip = append(cityStateZip, zip) + } + + if len(cityStateZip) > 0 { + parts = append(parts, strings.Join(cityStateZip, ", ")) + } + + return strings.Join(parts, ", ") +} + +func isCommonField(field string) bool { + commonFields := map[string]bool{ + "firstname": true, + "lastname": true, + "middlename": true, + "dob": true, + "ssn": true, + "phone1": true, + "address": true, + "city": true, + "st": true, + "zip": true, + } + return commonFields[field] +} -- cgit v1.2.3