From cfc032f3875da20eb2e6c82339219742acde3595 Mon Sep 17 00:00:00 2001 From: Abhinav Raut Date: Fri, 10 Jul 2026 17:07:50 +0530 Subject: [PATCH] add SplitName util and use it to parse email contact names --- internal/inbox/channel/email/imap.go | 10 +++------- internal/stringutil/stringutil.go | 12 ++++++++++++ internal/stringutil/stringutil_test.go | 26 ++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/internal/inbox/channel/email/imap.go b/internal/inbox/channel/email/imap.go index 3cfd725a..02602a14 100644 --- a/internal/inbox/channel/email/imap.go +++ b/internal/inbox/channel/email/imap.go @@ -548,15 +548,11 @@ func (e *Email) processFullMessage(item imapclient.FetchItemDataBodySection, inc // getContactName extracts the contact's first and last name from the IMAP address. func getContactName(imapAddr imap.Address) (string, string) { - from := strings.TrimSpace(imapAddr.Name) - names := strings.Fields(from) - if len(names) == 0 { + first, last := stringutil.SplitName(imapAddr.Name) + if first == "" { return imapAddr.Host, "" } - if len(names) == 1 { - return names[0], "" - } - return names[0], names[1] + return first, last } // isAutoReply checks if a given email envelope indicates an auto-reply message. diff --git a/internal/stringutil/stringutil.go b/internal/stringutil/stringutil.go index 349901f1..e9849e5b 100644 --- a/internal/stringutil/stringutil.go +++ b/internal/stringutil/stringutil.go @@ -237,3 +237,15 @@ func ExtractReferenceNumber(subject string) string { } return "" } + +// SplitName splits a full name; the first word is the first name, the rest is the last name. +func SplitName(name string) (string, string) { + fields := strings.Fields(name) + if len(fields) == 0 { + return "", "" + } + if len(fields) == 1 { + return fields[0], "" + } + return fields[0], strings.Join(fields[1:], " ") +} diff --git a/internal/stringutil/stringutil_test.go b/internal/stringutil/stringutil_test.go index bcd6100b..344084b4 100644 --- a/internal/stringutil/stringutil_test.go +++ b/internal/stringutil/stringutil_test.go @@ -270,3 +270,29 @@ func TestSanitizeUTF8(t *testing.T) { }) } } + +func TestSplitName(t *testing.T) { + tests := []struct { + name string + input string + wantFirst string + wantLast string + }{ + {name: "empty", input: "", wantFirst: "", wantLast: ""}, + {name: "whitespace only", input: " ", wantFirst: "", wantLast: ""}, + {name: "single name", input: "Cher", wantFirst: "Cher", wantLast: ""}, + {name: "first and last", input: "John Doe", wantFirst: "John", wantLast: "Doe"}, + {name: "middle name", input: "John Michael Doe", wantFirst: "John", wantLast: "Michael Doe"}, + {name: "multi-word surname", input: "Ludwig van der Berg", wantFirst: "Ludwig", wantLast: "van der Berg"}, + {name: "extra spaces", input: " John Doe ", wantFirst: "John", wantLast: "Doe"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + first, last := SplitName(tt.input) + if first != tt.wantFirst || last != tt.wantLast { + t.Errorf("SplitName(%q) = (%q, %q), want (%q, %q)", tt.input, first, last, tt.wantFirst, tt.wantLast) + } + }) + } +}