-
Notifications
You must be signed in to change notification settings - Fork 1
/
addresslist.go
164 lines (148 loc) · 3.56 KB
/
addresslist.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package email
import (
"errors"
"fmt"
"net/mail"
"strings"
"github.com/domonda/go-types/nullable"
)
// NormalizeAddressList parses an email address list less strict
// than the standard net/mail.ParseAddressList function
// fixing malformed addresses and lower cases the address part.
// Duplicates with the same normalized address part
// will be removed from the result.
// NormalizeAddressList returns an error if list does not contain
// at least one address.
func NormalizeAddressList(list string) (normalized []string, err error) {
addrs, err := ParseAddressList(list)
if err != nil {
return nil, err
}
appended := make(map[string]bool, len(addrs))
normalized = make([]string, 0, len(addrs))
for _, a := range addrs {
if appended[a.Address] {
continue
}
normalized = append(normalized, string(AddressFrom(a)))
appended[a.Address] = true
}
return normalized, nil
}
// AddressList is a comma separated list
// of at least one email address.
//
// Use NullableAddressList for a list
// that can contain zero addresses.
type AddressList string
func AddressListJoin(addrs ...Address) AddressList {
var b strings.Builder
for _, addr := range addrs {
if len(addr) == 0 {
continue
}
if b.Len() > 0 {
b.WriteString(", ")
}
b.WriteString(string(addr))
}
return AddressList(b.String())
}
func AddressListJoinStrings(addrs ...string) AddressList {
var b strings.Builder
for _, addr := range addrs {
if len(addr) == 0 {
continue
}
if b.Len() > 0 {
b.WriteString(", ")
}
b.WriteString(addr)
}
return AddressList(b.String())
}
func (l AddressList) Append(addrs ...Address) AddressList {
var b strings.Builder
b.WriteString(string(l))
for _, addr := range addrs {
if len(addr) == 0 {
continue
}
if b.Len() > 0 {
b.WriteString(", ")
}
b.WriteString(string(addr))
}
return AddressList(b.String())
}
func (l AddressList) Parse() ([]*mail.Address, error) {
return ParseAddressList(string(l))
}
func (l AddressList) Split() ([]Address, error) {
parsed, err := l.Parse()
if err != nil {
return nil, err
}
a := make([]Address, len(parsed))
for i, p := range parsed {
a[i] = AddressFrom(p)
}
return a, nil
}
func (l AddressList) UniqueAddressParts() (AddressSet, error) {
parsed, err := l.Parse()
if err != nil {
return nil, err
}
set := make(AddressSet, len(parsed))
for _, addr := range parsed {
set.Add(Address(addr.Address))
}
return set, nil
}
func (l AddressList) Validate() error {
_, err := l.Parse()
return err
}
func (l AddressList) Normalized() (AddressList, error) {
parsed, err := l.Parse()
if err != nil {
return "", err
}
b := strings.Builder{}
b.Grow(len(l))
for i, p := range parsed {
if i > 0 {
b.WriteString(", ")
}
b.WriteString(string(AddressFrom(p)))
}
return AddressList(b.String()), nil
}
func (l AddressList) Nullable() NullableAddressList {
return NullableAddressList(l)
}
// Scan implements the database/sql.Scanner interface.
// Supports scanning SQL strings and string arrays.
func (l *AddressList) Scan(value any) error {
switch s := value.(type) {
case string:
if s == "" {
return errors.New("can't scan empty string as email.AddressList")
}
if s[0] == '{' && s[len(s)-1] == '}' {
stringArray, err := nullable.SplitArray(s)
if err != nil {
return fmt.Errorf("can't scan SQL array string %q as email.AddressList because of: %w", s, err)
}
*l = AddressListJoinStrings(stringArray...)
return nil
}
*l = AddressList(s)
return nil
case []byte:
return l.Scan(string(s))
default:
return fmt.Errorf("can't scan %T as email.AddressList", value)
}
}