git » spf » commit 7a83916

Update the code to use modern Go features/functions

author Alberto Bertogli
2026-08-17 20:27:03 UTC
committer Alberto Bertogli
2026-08-22 08:58:09 UTC
parent 245e1de25f72e84bd569f3bc9f35efe00ff50b12

Update the code to use modern Go features/functions

Now that the minimum supported Go version is 1.25, this patch updates
the code to make use of some new features and functions to improve
readability.

There are no functional changes.

The Result values are now const instead of var, so callers can no longer
reassign them. Note this means "&spf.Pass" and assignments to it (and friends)
no longer compile. But neither are expected in this context, and a review of
all known users found no instances of problematic usage.

cmd/spf-check/spf-check.go +1 -1
spf.go +21 -27
spf_test.go +1 -1
yml_test.go +5 -9

diff --git a/cmd/spf-check/spf-check.go b/cmd/spf-check/spf-check.go
index 86c3d1e..2ac4d16 100644
--- a/cmd/spf-check/spf-check.go
+++ b/cmd/spf-check/spf-check.go
@@ -37,7 +37,7 @@ func main() {
 
 	opts := []spf.Option{}
 	if *debug {
-		traceF := func(f string, a ...interface{}) {
+		traceF := func(f string, a ...any) {
 			fmt.Printf("debug: "+f+"\n", a...)
 		}
 		opts = append(opts, spf.WithTraceFunc(traceF))
diff --git a/spf.go b/spf.go
index 3de4bf3..bc73e36 100644
--- a/spf.go
+++ b/spf.go
@@ -23,6 +23,7 @@ import (
 	"net"
 	"net/url"
 	"regexp"
+	"slices"
 	"strconv"
 	"strings"
 )
@@ -32,7 +33,7 @@ import (
 type Result string
 
 // Valid results.
-var (
+const (
 	// https://tools.ietf.org/html/rfc7208#section-8.1
 	// Not able to reach any conclusion.
 	None = Result("none")
@@ -114,10 +115,10 @@ const (
 )
 
 // TraceFunc is the type of tracing functions.
-type TraceFunc func(f string, a ...interface{})
+type TraceFunc func(f string, a ...any)
 
 var (
-	nullTrace    = func(f string, a ...interface{}) {}
+	nullTrace    = func(f string, a ...any) {}
 	defaultTrace = nullTrace
 )
 
@@ -147,7 +148,7 @@ func CheckHost(ip net.IP, domain string) (Result, error) {
 		maxvoidcount: defaultMaxVoidLookups,
 		helo:         domain,
 		sender:       "@" + domain,
-		ctx:          context.TODO(),
+		ctx:          context.Background(),
 		resolver:     defaultResolver,
 		trace:        defaultTrace,
 	}
@@ -195,7 +196,7 @@ func CheckHostWithSender(ip net.IP, helo, sender string, opts ...Option) (Result
 		maxvoidcount: defaultMaxVoidLookups,
 		helo:         helo,
 		sender:       sender,
-		ctx:          context.TODO(),
+		ctx:          context.Background(),
 		resolver:     defaultResolver,
 		trace:        defaultTrace,
 	}
@@ -481,13 +482,14 @@ func (r *resolution) getDNSRecord(domain string) (string, error) {
 	// 1 record is what we expect, return the record.
 	// More than that, it's a permanent error:
 	// https://tools.ietf.org/html/rfc7208#section-4.5
-	l := len(records)
-	if l == 0 {
+	switch len(records) {
+	case 0:
 		return "", nil
-	} else if l == 1 {
+	case 1:
 		return records[0], nil
+	default:
+		return "", ErrMultipleRecords
 	}
-	return "", ErrMultipleRecords
 }
 
 func isTemporary(err error) bool {
@@ -937,19 +939,19 @@ func (r *resolution) expandMacros(s, domain string) (string, error) {
 	macroS := ""
 
 	var err error
-	n := ""
+	var n strings.Builder
 	for _, c := range s {
 		if afterPercent {
 			afterPercent = false
 			switch c {
 			case '%':
-				n += "%"
+				n.WriteString("%")
 				continue
 			case '_':
-				n += " "
+				n.WriteString(" ")
 				continue
 			case '-':
-				n += "%20"
+				n.WriteString("%20")
 				continue
 			case '{':
 				inMacroDefinition = true
@@ -1033,14 +1035,12 @@ func (r *resolution) expandMacros(s, domain string) (string, error) {
 
 			// Reverse if requested.
 			if reverse {
-				reverseStrings(split)
+				slices.Reverse(split)
 			}
 
 			// Leave the last $digits fields, if given.
 			if digits > 0 {
-				if digits > len(split) {
-					digits = len(split)
-				}
+				digits = min(digits, len(split))
 				split = split[len(split)-digits:]
 			}
 
@@ -1054,24 +1054,18 @@ func (r *resolution) expandMacros(s, domain string) (string, error) {
 				str = url.QueryEscape(str)
 			}
 
-			n += str
+			n.WriteString(str)
 			continue
 		}
 		if c == '%' {
 			afterPercent = true
 			continue
 		}
-		n += string(c)
+		n.WriteString(string(c))
 	}
 
-	r.trace("macro expanded %q to %q", s, n)
-	return n, nil
-}
-
-func reverseStrings(a []string) {
-	for left, right := 0, len(a)-1; left < right; left, right = left+1, right-1 {
-		a[left], a[right] = a[right], a[left]
-	}
+	r.trace("macro expanded %q to %q", s, n.String())
+	return n.String(), nil
 }
 
 func ipToMacroStr(ip net.IP) string {
diff --git a/spf_test.go b/spf_test.go
index 8aa5cae..5ba1623 100644
--- a/spf_test.go
+++ b/spf_test.go
@@ -801,7 +801,7 @@ func TestBadResolverResponse(t *testing.T) {
 
 func TestWithTraceFunc(t *testing.T) {
 	calls := 0
-	var trace TraceFunc = func(f string, a ...interface{}) {
+	var trace TraceFunc = func(f string, a ...any) {
 		calls++
 		t.Logf("tracing "+f, a...)
 	}
diff --git a/yml_test.go b/yml_test.go
index e9f94f2..181e6ce 100644
--- a/yml_test.go
+++ b/yml_test.go
@@ -6,6 +6,7 @@ import (
 	"io"
 	"net"
 	"os"
+	"slices"
 	"strings"
 	"testing"
 
@@ -124,7 +125,7 @@ type MX struct {
 }
 
 func (mx *MX) UnmarshalYAML(value *yaml.Node) error {
-	seq := []interface{}{}
+	seq := []any{}
 	if err := value.Decode(&seq); err != nil {
 		return err
 	}
@@ -266,12 +267,7 @@ func testRFC(t *testing.T, fname string) {
 }
 
 func resultIn(got Result, exp []string) bool {
-	for _, e := range exp {
-		if e == string(got) {
-			return true
-		}
-	}
-	return false
+	return slices.Contains(exp, string(got))
 }
 
 // Take a reverse-dns host name of the form:
@@ -288,7 +284,7 @@ func reverseDNS(t *testing.T, r string) net.IP {
 
 		// Break down in pieces, and construct the ipv4 string backwards.
 		pieces := strings.Split(r, ".")
-		for i := 0; i < len(pieces); i++ {
+		for i := range pieces {
 			s += pieces[len(pieces)-1-i] + "."
 		}
 		s = s[:len(s)-1]
@@ -298,7 +294,7 @@ func reverseDNS(t *testing.T, r string) net.IP {
 
 		// Break down in pieces, and construct the ipv6 string backwards.
 		pieces := strings.Split(r, ".")
-		for i := 0; i < len(pieces); i++ {
+		for i := range pieces {
 			s += pieces[len(pieces)-1-i]
 			if i%4 == 3 {
 				s += ":"