diff --git a/config/tags.go b/config/tags.go index 080a846..6b152f9 100644 --- a/config/tags.go +++ b/config/tags.go @@ -94,9 +94,9 @@ func parseTagsDisable(s string) error { return nil } - parts := strings.Split(strings.ToLower(s), ",") + parts := strings.SplitSeq(strings.ToLower(s), ",") - for _, p := range parts { + for p := range parts { switch strings.TrimSpace(p) { case "x-tags", "xtags": TagsDisableXTags = true diff --git a/config/validators.go b/config/validators.go index fe01328..c9b1d8a 100644 --- a/config/validators.go +++ b/config/validators.go @@ -26,8 +26,8 @@ func parseMaxAge() error { return fmt.Errorf("max-age must be either h for hours or d for days: %s", MaxAge) } - if strings.HasSuffix(MaxAge, "h") { - hours, err := strconv.Atoi(strings.TrimSuffix(MaxAge, "h")) + if before, ok := strings.CutSuffix(MaxAge, "h"); ok { + hours, err := strconv.Atoi(before) if err != nil { return err } @@ -221,8 +221,8 @@ func validateForwardConfig() error { } to := []string{} - addresses := strings.Split(SMTPForwardConfig.To, ",") - for _, a := range addresses { + addresses := strings.SplitSeq(SMTPForwardConfig.To, ",") + for a := range addresses { a = strings.TrimSpace(a) m, err := mail.ParseAddress(a) if err != nil { @@ -263,8 +263,8 @@ func parseChaosTriggers() error { re := regexp.MustCompile(`^([a-zA-Z0-0]+):(\d\d\d):(\d+(\.\d)?)$`) - parts := strings.Split(ChaosTriggers, ",") - for _, p := range parts { + parts := strings.SplitSeq(ChaosTriggers, ",") + for p := range parts { p = strings.TrimSpace(p) if !re.MatchString(p) { return fmt.Errorf("invalid argument: %s", p) diff --git a/internal/htmlcheck/caniemail.go b/internal/htmlcheck/caniemail.go index 8b33acd..4d4c947 100644 --- a/internal/htmlcheck/caniemail.go +++ b/internal/htmlcheck/caniemail.go @@ -42,19 +42,19 @@ type CanIEmail struct { // JSONResult struct for CanIEmail Data type JSONResult struct { - Slug string `json:"slug"` - Title string `json:"title"` - Description string `json:"description"` - URL string `json:"url"` - Category string `json:"category"` - Tags []string `json:"tags"` - Keywords string `json:"keywords"` - LastTestDate string `json:"last_test_date"` - TestURL string `json:"test_url"` - TestResultsURL string `json:"test_results_url"` - Stats map[string]interface{} `json:"stats"` - Notes string `json:"notes"` - NotesByNumber map[string]string `json:"notes_by_num"` + Slug string `json:"slug"` + Title string `json:"title"` + Description string `json:"description"` + URL string `json:"url"` + Category string `json:"category"` + Tags []string `json:"tags"` + Keywords string `json:"keywords"` + LastTestDate string `json:"last_test_date"` + TestURL string `json:"test_url"` + TestResultsURL string `json:"test_results_url"` + Stats map[string]any `json:"stats"` + Notes string `json:"notes"` + NotesByNumber map[string]string `json:"notes_by_num"` } // Load the JSON data diff --git a/internal/htmlcheck/inline_test.go b/internal/htmlcheck/inline_test.go index 02fe574..8843f19 100644 --- a/internal/htmlcheck/inline_test.go +++ b/internal/htmlcheck/inline_test.go @@ -72,7 +72,7 @@ func TestInlineStyleDetection(t *testing.T) { } } -func assertEqual(t *testing.T, a interface{}, b interface{}, message string) { +func assertEqual(t *testing.T, a any, b any, message string) { if a == b { return } diff --git a/internal/htmlcheck/main.go b/internal/htmlcheck/main.go index aad4a95..87611fd 100644 --- a/internal/htmlcheck/main.go +++ b/internal/htmlcheck/main.go @@ -141,11 +141,11 @@ func (c CanIEmail) getTest(k string) (Warning, error) { continue } - for platform, clients := range stats.(map[string]interface{}) { + for platform, clients := range stats.(map[string]any) { if len(LimitPlatforms) != 0 && !tools.InArray(platform, LimitPlatforms) { continue } - for version, support := range clients.(map[string]interface{}) { + for version, support := range clients.(map[string]any) { s := Result{} s.Name = fmt.Sprintf("%s %s (%s)", c.NiceNames.Family[family], c.NiceNames.Platform[platform], version) s.Family = family diff --git a/internal/htmlcheck/platforms.go b/internal/htmlcheck/platforms.go index 42c0c79..e72eaea 100644 --- a/internal/htmlcheck/platforms.go +++ b/internal/htmlcheck/platforms.go @@ -1,7 +1,7 @@ package htmlcheck import ( - "sort" + "slices" "github.com/axllent/mailpit/internal/tools" ) @@ -18,7 +18,7 @@ func Platforms() (map[string][]string, error) { for _, t := range cie.Data { for family, stats := range t.Stats { niceFamily := cie.NiceNames.Family[family] - for platform := range stats.(map[string]interface{}) { + for platform := range stats.(map[string]any) { c, found := data[platform] if !found { data[platform] = []string{} @@ -32,9 +32,7 @@ func Platforms() (map[string][]string, error) { } for group, clients := range data { - sort.Slice(clients, func(i, j int) bool { - return clients[i] < clients[j] - }) + slices.Sort(clients) data[group] = clients } diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 88b31ec..0132be2 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -61,7 +61,7 @@ func Log() *logrus.Logger { } // PrettyPrint for debugging -func PrettyPrint(i interface{}) { +func PrettyPrint(i any) { s, _ := json.MarshalIndent(i, "", "\t") fmt.Println(string(s)) } diff --git a/internal/pop3/pop3_test.go b/internal/pop3/pop3_test.go index cbaad5a..d6eff55 100644 --- a/internal/pop3/pop3_test.go +++ b/internal/pop3/pop3_test.go @@ -362,11 +362,11 @@ func randRange(min, max int) int { } func insertEmailData(t *testing.T) { - for i := 0; i < 50; i++ { + for i := range 50 { msg := enmime.Builder(). From(fmt.Sprintf("From %d", i), fmt.Sprintf("from-%d@example.com", i)). Subject(fmt.Sprintf("Subject line %d end", i)). - Text([]byte(fmt.Sprintf("This is the email body %d .", i))). + Text(fmt.Appendf(nil, "This is the email body %d .", i)). To(fmt.Sprintf("To %d", i), fmt.Sprintf("to-%d@example.com", i)) env, err := msg.Build() @@ -397,7 +397,7 @@ func insertEmailData(t *testing.T) { } } -func assertEqual(t *testing.T, a interface{}, b interface{}, message string) { +func assertEqual(t *testing.T, a any, b any, message string) { if a == b { return } diff --git a/internal/pop3client/client.go b/internal/pop3client/client.go index a048cf1..31feae6 100644 --- a/internal/pop3client/client.go +++ b/internal/pop3client/client.go @@ -138,7 +138,7 @@ func (c *Conn) Send(b string) error { // in case of single line responses, or a help message followed by multiple lines of actual response // data in case of multiline responses. // See https://www.shellhacks.com/retrieve-email-pop3-server-command-line/ for examples. -func (c *Conn) Cmd(cmd string, isMulti bool, args ...interface{}) (*bytes.Buffer, error) { +func (c *Conn) Cmd(cmd string, isMulti bool, args ...any) (*bytes.Buffer, error) { var cmdLine string // Repeat a %v to format each arg. @@ -441,12 +441,12 @@ func parseResp(b []byte) ([]byte, error) { if bytes.Equal(b, respOK) { return nil, nil - } else if bytes.HasPrefix(b, respOKInfo) { - return bytes.TrimPrefix(b, respOKInfo), nil + } else if after, ok := bytes.CutPrefix(b, respOKInfo); ok { + return after, nil } else if bytes.Equal(b, respErr) { return nil, errors.New("unknown error (no info specified in response)") - } else if bytes.HasPrefix(b, respErrInfo) { - return nil, errors.New(string(bytes.TrimPrefix(b, respErrInfo))) + } else if after, ok := bytes.CutPrefix(b, respErrInfo); ok { + return nil, errors.New(string(after)) } return nil, fmt.Errorf("unknown response: %s. Neither -ERR, nor +OK", string(b)) diff --git a/internal/smtpd/forward.go b/internal/smtpd/forward.go index 56baf60..c38a133 100644 --- a/internal/smtpd/forward.go +++ b/internal/smtpd/forward.go @@ -108,9 +108,9 @@ func forward(from string, msg []byte) error { return fmt.Errorf("error response to MAIL command: %s", err.Error()) } - to := strings.Split(config.SMTPForwardConfig.To, ",") + to := strings.SplitSeq(config.SMTPForwardConfig.To, ",") - for _, addr := range to { + for addr := range to { if err = c.Rcpt(addr); err != nil { logger.Log().Warnf("error response to RCPT command for %s: %s", addr, err.Error()) if config.SMTPForwardConfig.ForwardSMTPErrors { diff --git a/internal/smtpd/smtpd.go b/internal/smtpd/smtpd.go index 43e19b9..1915f81 100644 --- a/internal/smtpd/smtpd.go +++ b/internal/smtpd/smtpd.go @@ -337,7 +337,7 @@ func (srv *Server) Shutdown(ctx context.Context) error { timer := time.NewTimer(100 * time.Millisecond) defer timer.Stop() - for i := 0; i < 300; i++ { + for range 300 { // wait for open sessions to close if atomic.LoadInt32(&srv.openSessions) == 0 { break @@ -636,8 +636,8 @@ loop: case "XCLIENT": s.xClient = args if s.xClientTrust { - xCArgs := strings.Split(args, " ") - for _, xCArg := range xCArgs { + xCArgs := strings.SplitSeq(args, " ") + for xCArg := range xCArgs { xCParse := strings.Split(strings.TrimSpace(xCArg), "=") if strings.ToUpper(xCParse[0]) == "ADDR" && (net.ParseIP(xCParse[1]) != nil) { s.xClientADDR = xCParse[1] @@ -786,7 +786,7 @@ loop: } // Wrapper function for writing a complete line to the socket. -func (s *session) writef(format string, args ...interface{}) { +func (s *session) writef(format string, args ...any) { if s.srv.Timeout > 0 { _ = s.conn.SetWriteDeadline(time.Now().Add(s.srv.Timeout)) } @@ -831,9 +831,9 @@ func (s *session) readLine() (string, error) { // Parse a line read from the socket. func (s *session) parseLine(line string) (verb string, args string) { - if idx := strings.Index(line, " "); idx != -1 { - verb = strings.ToUpper(line[:idx]) - args = strings.TrimSpace(line[idx+1:]) + if before, after, ok := strings.Cut(line, " "); ok { + verb = strings.ToUpper(before) + args = strings.TrimSpace(after) } else { verb = strings.ToUpper(line) args = "" diff --git a/internal/smtpd/smtpd_test.go b/internal/smtpd/smtpd_test.go index 45b77f7..98a462c 100644 --- a/internal/smtpd/smtpd_test.go +++ b/internal/smtpd/smtpd_test.go @@ -779,8 +779,8 @@ func parseExtensions(t *testing.T, greeting string) map[string]string { // Add line as extension. line = strings.TrimSpace(line[4:]) // Strip code prefix and trailing \r\n - if idx := strings.Index(line, " "); idx != -1 { - extensions[line[:idx]] = line[idx+1:] + if before, after, ok := strings.Cut(line, " "); ok { + extensions[before] = after } else { extensions[line] = "" } diff --git a/internal/storage/cron.go b/internal/storage/cron.go index 7e789c0..c6de0b1 100644 --- a/internal/storage/cron.go +++ b/internal/storage/cron.go @@ -128,7 +128,7 @@ func pruneMessages() { return } - args := make([]interface{}, len(ids)) + args := make([]any, len(ids)) for i, id := range ids { args[i] = id } diff --git a/internal/storage/functions_test.go b/internal/storage/functions_test.go index dc98da8..fc126c1 100644 --- a/internal/storage/functions_test.go +++ b/internal/storage/functions_test.go @@ -49,7 +49,7 @@ func setup(tenantID string) { } } -func assertEqual(t *testing.T, a interface{}, b interface{}, message string) { +func assertEqual(t *testing.T, a any, b any, message string) { if a == b { return } diff --git a/internal/storage/messages.go b/internal/storage/messages.go index 1b41961..3a24a71 100644 --- a/internal/storage/messages.go +++ b/internal/storage/messages.go @@ -656,7 +656,7 @@ func DeleteMessages(ids []string) error { start := time.Now() - args := make([]interface{}, len(ids)) + args := make([]any, len(ids)) for i, id := range ids { args[i] = id } @@ -696,7 +696,7 @@ func DeleteMessages(ids []string) error { return err } - args = make([]interface{}, len(toDelete)) + args = make([]any, len(toDelete)) for i, id := range toDelete { args[i] = id } diff --git a/internal/storage/messages_test.go b/internal/storage/messages_test.go index 4f46cbe..0676f6a 100644 --- a/internal/storage/messages_test.go +++ b/internal/storage/messages_test.go @@ -16,7 +16,7 @@ func TestTextEmailInserts(t *testing.T) { start := time.Now() - for i := 0; i < testRuns; i++ { + for range testRuns { if _, err := Store(&testTextEmail, nil); err != nil { t.Log("error ", err) t.Fail() @@ -54,7 +54,7 @@ func TestMimeEmailInserts(t *testing.T) { start := time.Now() - for i := 0; i < testRuns; i++ { + for range testRuns { if _, err := Store(&testMimeEmail, nil); err != nil { t.Log("error ", err) t.Fail() diff --git a/internal/storage/search.go b/internal/storage/search.go index 252b4c9..65a7340 100644 --- a/internal/storage/search.go +++ b/internal/storage/search.go @@ -80,10 +80,7 @@ func Search(search, timezone string, start int, beforeTS int64, limit int) ([]Me nrResults = len(allResults) if nrResults > start { - end := nrResults - if nrResults >= start+limit { - end = start + limit - } + end := min(nrResults, start+limit) results = allResults[start:end] } @@ -196,7 +193,7 @@ func DeleteSearch(search, timezone string) error { defer func() { _ = tx.Rollback() }() for _, ids := range chunks { - delIDs := make([]interface{}, len(ids)) + delIDs := make([]any, len(ids)) for i, id := range ids { delIDs[i] = id } diff --git a/internal/storage/search_test.go b/internal/storage/search_test.go index f139ed5..848581d 100644 --- a/internal/storage/search_test.go +++ b/internal/storage/search_test.go @@ -22,13 +22,13 @@ func TestSearch(t *testing.T) { t.Logf("Testing search (tenant %s)", tenantID) } - for i := 0; i < testRuns; i++ { + for i := range testRuns { msg := enmime.Builder(). From(fmt.Sprintf("From %d", i), fmt.Sprintf("from-%d@example.com", i)). CC(fmt.Sprintf("CC %d", i), fmt.Sprintf("cc-%d@example.com", i)). CC(fmt.Sprintf("CC2 %d", i), fmt.Sprintf("cc2-%d@example.com", i)). Subject(fmt.Sprintf("Subject line %d end", i)). - Text([]byte(fmt.Sprintf("This is the email body %d .", i))). + Text(fmt.Appendf(nil, "This is the email body %d .", i)). To(fmt.Sprintf("To %d", i), fmt.Sprintf("to-%d@example.com", i)). To(fmt.Sprintf("To2 %d", i), fmt.Sprintf("to2-%d@example.com", i)). ReplyTo(fmt.Sprintf("Reply To %d", i), fmt.Sprintf("reply-to-%d@example.com", i)) @@ -116,7 +116,7 @@ func TestSearchDelete100(t *testing.T) { t.Logf("Testing search delete of 100 messages (tenant %s)", tenantID) } - for i := 0; i < 100; i++ { + for range 100 { if _, err := Store(&testTextEmail, nil); err != nil { t.Log("error ", err) t.Fail() @@ -157,7 +157,7 @@ func TestSearchDelete1100(t *testing.T) { defer Close() t.Log("Testing search delete of 1100 messages") - for i := 0; i < 1100; i++ { + for range 1100 { if _, err := Store(&testTextEmail, nil); err != nil { t.Log("error ", err) t.Fail() diff --git a/internal/storage/tags_test.go b/internal/storage/tags_test.go index c12cc1b..ba68ed3 100644 --- a/internal/storage/tags_test.go +++ b/internal/storage/tags_test.go @@ -3,6 +3,7 @@ package storage import ( "context" "fmt" + "slices" "strings" "testing" @@ -25,7 +26,7 @@ func TestTags(t *testing.T) { ids := []string{} - for i := 0; i < 10; i++ { + for range 10 { id, err := Store(&testMimeEmail, nil) if err != nil { t.Log("error ", err) @@ -34,14 +35,14 @@ func TestTags(t *testing.T) { ids = append(ids, id) } - for i := 0; i < 10; i++ { + for i := range 10 { if _, err := SetMessageTags(ids[i], []string{fmt.Sprintf("Tag-%d", i)}); err != nil { t.Log("error ", err) t.Fail() } } - for i := 0; i < 10; i++ { + for i := range 10 { message, err := GetMessage(ids[i]) if err != nil { t.Log("error ", err) @@ -65,7 +66,7 @@ func TestTags(t *testing.T) { t.Fail() } newTags := []string{} - for i := 0; i < 20; i++ { + for i := range 20 { // pad number with 0 to ensure they are returned alphabetically newTags = append(newTags, fmt.Sprintf("AnotherTag %02d", i)) } @@ -159,13 +160,7 @@ func TestUsernameAutoTagging(t *testing.T) { if err != nil { t.Fatalf("GetMessage failed: %v", err) } - found := false - for _, tag := range msg.Tags { - if tag == username { - found = true - break - } - } + found := slices.Contains(msg.Tags, username) if !found { t.Errorf("Expected username '%s' in tags, got %v", username, msg.Tags) } diff --git a/server/server_test.go b/server/server_test.go index 0ff9934..5d3b4e0 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -28,7 +28,7 @@ var ( } // Shared test message structure for consistency - testSendMessage = map[string]interface{}{ + testSendMessage = map[string]any{ "From": map[string]string{ "Email": "test@example.com", }, @@ -545,11 +545,11 @@ func assertSearchEqual(t *testing.T, uri, query string, count int) { } func insertEmailData(t *testing.T) { - for i := 0; i < 100; i++ { + for i := range 100 { msg := enmime.Builder(). From(fmt.Sprintf("From %d", i), fmt.Sprintf("from-%d@example.com", i)). Subject(fmt.Sprintf("Subject line %d end", i)). - Text([]byte(fmt.Sprintf("This is the email body %d .", i))). + Text(fmt.Appendf(nil, "This is the email body %d .", i)). To(fmt.Sprintf("To %d", i), fmt.Sprintf("to-%d@example.com", i)) env, err := msg.Build() @@ -754,7 +754,7 @@ func clientGetWithAuth(url, username, password string) ([]byte, error) { return data, err } -func assertEqual(t *testing.T, a interface{}, b interface{}, message string) { +func assertEqual(t *testing.T, a any, b any, message string) { if a == b { return } diff --git a/server/websockets/client.go b/server/websockets/client.go index c56ecd1..43017bf 100644 --- a/server/websockets/client.go +++ b/server/websockets/client.go @@ -98,7 +98,7 @@ func (c *Client) writePump() { // Add queued chat messages to the current websocket message. n := len(c.send) - for i := 0; i < n; i++ { + for range n { _, _ = w.Write(newline) _, _ = w.Write(<-c.send) } diff --git a/server/websockets/hub.go b/server/websockets/hub.go index e50a9f0..2db98e2 100644 --- a/server/websockets/hub.go +++ b/server/websockets/hub.go @@ -27,7 +27,7 @@ type Hub struct { // WebsocketNotification struct for responses type WebsocketNotification struct { Type string - Data interface{} + Data any } // NewHub returns a new hub configuration @@ -69,7 +69,7 @@ func (h *Hub) Run() { } // Broadcast will spawn a broadcast message to all connected clients -func Broadcast(t string, msg interface{}) { +func Broadcast(t string, msg any) { if MessageHub == nil || len(MessageHub.Clients) == 0 { return }