50 lines
1.4 KiB
Go
50 lines
1.4 KiB
Go
package valueobject
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
)
|
|
|
|
// DomainName is an RFC 1035 fully-qualified domain name, normalized to lowercase.
|
|
type DomainName struct {
|
|
value string
|
|
}
|
|
|
|
var (
|
|
ErrDomainNameEmpty = errors.New("domain name: must not be empty")
|
|
ErrDomainNameNotFQDN = errors.New("domain name: must contain at least one dot (not a FQDN)")
|
|
ErrDomainNameInvalidLabel = errors.New("domain name: label must not start or end with a hyphen")
|
|
)
|
|
|
|
// NewDomainName validates and normalizes raw into a DomainName.
|
|
func NewDomainName(raw string) (DomainName, error) {
|
|
if raw == "" {
|
|
return DomainName{}, ErrDomainNameEmpty
|
|
}
|
|
lower := strings.ToLower(raw)
|
|
labels := strings.Split(lower, ".")
|
|
if len(labels) < 2 {
|
|
return DomainName{}, ErrDomainNameNotFQDN
|
|
}
|
|
for _, label := range labels {
|
|
if label == "" || strings.HasPrefix(label, "-") || strings.HasSuffix(label, "-") {
|
|
return DomainName{}, ErrDomainNameInvalidLabel
|
|
}
|
|
}
|
|
return DomainName{value: lower}, nil
|
|
}
|
|
|
|
func (d DomainName) String() string { return d.value }
|
|
|
|
// IsPunycode reports whether the domain name is IDNA/punycode-encoded.
|
|
// It returns true if any label of the domain starts with the ASCII Compatible Encoding prefix "xn--".
|
|
func (d DomainName) IsPunycode() bool {
|
|
labels := strings.Split(d.value, ".")
|
|
for _, label := range labels {
|
|
if strings.HasPrefix(label, "xn--") {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|