ParsePatchIdentity parses a patch identity string. A patch identity contains an email address and an optional name in [RFC 5322] format. This is either a plain email adddress or a name followed by an address in angle brackets: author@example.com Author Name If the input is n
(s string)
| 33 | // |
| 34 | // [RFC 5322]: https://datatracker.ietf.org/doc/html/rfc5322 |
| 35 | func ParsePatchIdentity(s string) (PatchIdentity, error) { |
| 36 | s = normalizeSpace(s) |
| 37 | s = unquotePairs(s) |
| 38 | |
| 39 | var name, email string |
| 40 | if at := strings.IndexByte(s, '@'); at >= 0 { |
| 41 | start, end := at, at |
| 42 | for start >= 0 && !isRFC5332Space(s[start]) && s[start] != '<' { |
| 43 | start-- |
| 44 | } |
| 45 | for end < len(s) && !isRFC5332Space(s[end]) && s[end] != '>' { |
| 46 | end++ |
| 47 | } |
| 48 | email = s[start+1 : end] |
| 49 | |
| 50 | // Adjust the boundaries so that we drop angle brackets, but keep |
| 51 | // spaces when removing the email to form the name. |
| 52 | if start < 0 || s[start] != '<' { |
| 53 | start++ |
| 54 | } |
| 55 | if end >= len(s) || s[end] != '>' { |
| 56 | end-- |
| 57 | } |
| 58 | name = s[:start] + s[end+1:] |
| 59 | } else { |
| 60 | start, end := 0, 0 |
| 61 | for i := 0; i < len(s); i++ { |
| 62 | if s[i] == '<' && start == 0 { |
| 63 | start = i + 1 |
| 64 | } |
| 65 | if s[i] == '>' && start > 0 { |
| 66 | end = i |
| 67 | break |
| 68 | } |
| 69 | } |
| 70 | if start > 0 && end >= start { |
| 71 | email = strings.TrimSpace(s[start:end]) |
| 72 | name = s[:start-1] |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | // After extracting the email, the name might contain extra whitespace |
| 77 | // again and may be surrounded by comment characters. The git source gives |
| 78 | // these examples of when this can happen: |
| 79 | // |
| 80 | // "Name <email@domain>" |
| 81 | // "email@domain (Name)" |
| 82 | // "Name <email@domain> (Comment)" |
| 83 | // |
| 84 | name = normalizeSpace(name) |
| 85 | if strings.HasPrefix(name, "(") && strings.HasSuffix(name, ")") { |
| 86 | name = name[1 : len(name)-1] |
| 87 | } |
| 88 | name = strings.TrimSpace(name) |
| 89 | |
| 90 | // If the name is empty or contains email-like characters, use the email |
| 91 | // instead (assuming one exists) |
| 92 | if name == "" || strings.ContainsAny(name, "@<>") { |