summaryrefslogtreecommitdiffstats
path: root/internal/formatter/npd.go
diff options
context:
space:
mode:
authors <[email protected]>2025-11-13 14:43:15 -0500
committers <[email protected]>2025-11-13 14:43:15 -0500
commit344a6f6415c3c1b593677adec3b8844e0839971b (patch)
treeb05291ecdf21917b27e9e234eeb997c2706966d5 /internal/formatter/npd.go
parenta5fc01a03753c9a18ddeaf13610dd99b4b311b80 (diff)
downloaddborg-344a6f6415c3c1b593677adec3b8844e0839971b.tar.gz
dborg-344a6f6415c3c1b593677adec3b8844e0839971b.zip
created pretty printing for all commandsv1.0.0
Diffstat (limited to 'internal/formatter/npd.go')
-rw-r--r--internal/formatter/npd.go139
1 files changed, 139 insertions, 0 deletions
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]
+}