From 329257be8d9fb05d3dcea49823acea0f878ed52c Mon Sep 17 00:00:00 2001 From: Sam Anthony Date: Mon, 11 May 2026 16:45:09 -0400 Subject: validate email and phone number --- ship.go | 84 ++++++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 22 deletions(-) (limited to 'ship.go') diff --git a/ship.go b/ship.go index b5aa822..d76661a 100644 --- a/ship.go +++ b/ship.go @@ -3,10 +3,14 @@ package lulu import ( "encoding/json" "fmt" + email "net/mail" + "regexp" ) //go:generate go run github.com/yawnak/string-enumer -t ShippingLevel -t Title --text -o ./ship_gen.go . +var phoneExpr = regexp.MustCompile(`^\+?[\d\s\-.\/()]{8,20}$`) + // ShippingLevel is the quality/speed with which a package is shipped. type ShippingLevel string @@ -28,32 +32,32 @@ type ShippingAddress struct { IsBusiness bool // Only relevant for US addresses. Some US carriers don't deliver to business-addresses on Saturday. Name string // Full name of the person, including first and last name. Title Title - Organization string // Name of an organization. Required if no person name is given. - Email string // Shipping carriers require an email address for notifications or handling delivery issues. If no email is given, the default email in the user profile will be used. - Phone string // Shipping carriers require a phone number for handling delivery issues. If no phone number is given, the default in the API user profile will be used. - TaxId string // The recipient’s tax identification number. Required for shipping addresses to Brazil, Chile, and Mexico. + Organization string // Name of an organization. Required if no person name is given. + Email *email.Address // Shipping carriers require an email address for notifications or handling delivery issues. If no email is given, the default email in the user profile will be used. + Phone PhoneNumber // Shipping carriers require a phone number for handling delivery issues. If no phone number is given, the default in the API user profile will be used. + TaxId string // The recipient’s tax identification number. Required for shipping addresses to Brazil, Chile, and Mexico. } // They use "country" in some places, "country_code" in others, etc. // This is the union of all of them. type shippingAddress struct { - Country string `json:"country"` - CountryCode string `json:"country_code"` - State string `json:"state"` - StateCode string `json:"state_code"` - City string `json:"city"` - Street1 string `json:"street1"` - Street2 string `json:"street2"` - PostCode string `json:"postcode"` - IsBusiness bool `json:"is_business"` - Name string `json:"name"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Title Title `json:"title"` - Organization string `json:"organization"` - Email string `json:"email"` - Phone string `json:"phone_number"` - TaxId string `json:"recipient_tax_id"` + Country string `json:"country"` + CountryCode string `json:"country_code"` + State string `json:"state"` + StateCode string `json:"state_code"` + City string `json:"city"` + Street1 string `json:"street1"` + Street2 string `json:"street2"` + PostCode string `json:"postcode"` + IsBusiness bool `json:"is_business"` + Name string `json:"name"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + Title Title `json:"title"` + Organization string `json:"organization"` + Email string `json:"email"` + Phone PhoneNumber `json:"phone_number"` + TaxId string `json:"recipient_tax_id"` } func (a *ShippingAddress) UnmarshalJSON(data []byte) error { @@ -98,7 +102,15 @@ func (a *ShippingAddress) UnmarshalJSON(data []byte) error { a.Title = alias.Title a.Organization = alias.Organization - a.Email = alias.Email + + if alias.Email != "" { + addr, err := email.ParseAddress(alias.Email) + if err != nil { + return err + } + a.Email = addr + } + a.Phone = alias.Phone a.TaxId = alias.TaxId @@ -123,3 +135,31 @@ const ( Ms Title = "MS" Dr Title = "DR" ) + +type PhoneNumber string + +func ParsePhoneNumber(s string) (PhoneNumber, error) { + if phoneExpr.MatchString(s) { + return PhoneNumber(s), nil + } + return "", fmt.Errorf("malformed phone number %q; must fit pattern `%s`", s, phoneExpr.String()) +} + +// MustParsePhoneNumber is like ParsePhoneNumber but panics if the phone +// number cannot be parsed. +func MustParsePhoneNumber(s string) PhoneNumber { + n, err := ParsePhoneNumber(s) + if err != nil { + panic(fmt.Sprintf("lulu: ParsePhoneNumber(%q): %v", s, err)) + } + return n +} + +func (n *PhoneNumber) UnmarshalText(text []byte) error { + pn, err := ParsePhoneNumber(string(text)) + if err != nil { + return err + } + *n = pn + return nil +} -- cgit v1.2.3