Skip to content

Commit

Permalink
add context support to pretty much everything and unexport some stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
DeedleFake committed Nov 29, 2021
1 parent 6dfbd25 commit 7f421f5
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 148 deletions.
193 changes: 99 additions & 94 deletions example_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package gomail_test

import (
"fmt"
"context"
"html/template"
"io"
"log"
"time"

"github.com/DeedleFake/gomail"
Expand All @@ -21,83 +20,86 @@ func Example() {

d := gomail.NewDialer("smtp.example.com", 587, "user", "123456")

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

// Send the email to Bob, Cora and Dan.
if err := d.DialAndSend(m); err != nil {
if err := d.DialAndSend(ctx, m); err != nil {
panic(err)
}
}

// A daemon that listens to a channel and sends all incoming messages.
func Example_daemon() {
ch := make(chan *gomail.Message)

go func() {
d := gomail.NewDialer("smtp.example.com", 587, "user", "123456")

var s gomail.SendCloser
var err error
open := false
for {
select {
case m, ok := <-ch:
if !ok {
return
}
if !open {
if s, err = d.Dial(); err != nil {
panic(err)
}
open = true
}
if err := gomail.Send(s, m); err != nil {
log.Print(err)
}
// Close the connection to the SMTP server if no email was sent in
// the last 30 seconds.
case <-time.After(30 * time.Second):
if open {
if err := s.Close(); err != nil {
panic(err)
}
open = false
}
}
}
}()

// Use the channel in your program to send emails.

// Close the channel to stop the mail daemon.
close(ch)
}
//func Example_daemon() {
// ch := make(chan *gomail.Message)
//
// go func() {
// d := gomail.NewDialer("smtp.example.com", 587, "user", "123456")
//
// var s gomail.SendCloser
// var err error
// open := false
// for {
// select {
// case m, ok := <-ch:
// if !ok {
// return
// }
// if !open {
// if s, err = d.Dial(); err != nil {
// panic(err)
// }
// open = true
// }
// if err := gomail.Send(s, m); err != nil {
// log.Print(err)
// }
// // Close the connection to the SMTP server if no email was sent in
// // the last 30 seconds.
// case <-time.After(30 * time.Second):
// if open {
// if err := s.Close(); err != nil {
// panic(err)
// }
// open = false
// }
// }
// }
// }()
//
// // Use the channel in your program to send emails.
//
// // Close the channel to stop the mail daemon.
// close(ch)
//}

// Efficiently send a customized newsletter to a list of recipients.
func Example_newsletter() {
// The list of recipients.
var list []struct {
Name string
Address string
}

d := gomail.NewDialer("smtp.example.com", 587, "user", "123456")
s, err := d.Dial()
if err != nil {
panic(err)
}

m := gomail.NewMessage()
for _, r := range list {
m.SetHeader("From", "[email protected]")
m.SetAddressHeader("To", r.Address, r.Name)
m.SetHeader("Subject", "Newsletter #1")
m.SetBody("text/html", fmt.Sprintf("Hello %s!", r.Name))

if err := gomail.Send(s, m); err != nil {
log.Printf("Could not send email to %q: %v", r.Address, err)
}
m.Reset()
}
}
//func Example_newsletter() {
// // The list of recipients.
// var list []struct {
// Name string
// Address string
// }
//
// d := gomail.NewDialer("smtp.example.com", 587, "user", "123456")
// s, err := d.Dial()
// if err != nil {
// panic(err)
// }
//
// m := gomail.NewMessage()
// for _, r := range list {
// m.SetHeader("From", "[email protected]")
// m.SetAddressHeader("To", r.Address, r.Name)
// m.SetHeader("Subject", "Newsletter #1")
// m.SetBody("text/html", fmt.Sprintf("Hello %s!", r.Name))
//
// if err := gomail.Send(s, m); err != nil {
// log.Printf("Could not send email to %q: %v", r.Address, err)
// }
// m.Reset()
// }
//}

// Send an email using a local SMTP server.
func Example_noAuth() {
Expand All @@ -107,35 +109,38 @@ func Example_noAuth() {
m.SetHeader("Subject", "Hello!")
m.SetBody("text/plain", "Hello!")

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

d := gomail.Dialer{Host: "localhost", Port: 587}
if err := d.DialAndSend(m); err != nil {
if err := d.DialAndSend(ctx, m); err != nil {
panic(err)
}
}

// Send an email using an API or postfix.
func Example_noSMTP() {
m := gomail.NewMessage()
m.SetHeader("From", "[email protected]")
m.SetHeader("To", "[email protected]")
m.SetHeader("Subject", "Hello!")
m.SetBody("text/plain", "Hello!")

s := gomail.SendFunc(func(from string, to []string, msg io.WriterTo) error {
// Implements you email-sending function, for example by calling
// an API, or running postfix, etc.
fmt.Println("From:", from)
fmt.Println("To:", to)
return nil
})

if err := gomail.Send(s, m); err != nil {
panic(err)
}
// Output:
// From: [email protected]
// To: [[email protected]]
}
//func Example_noSMTP() {
// m := gomail.NewMessage()
// m.SetHeader("From", "[email protected]")
// m.SetHeader("To", "[email protected]")
// m.SetHeader("Subject", "Hello!")
// m.SetBody("text/plain", "Hello!")
//
// s := gomail.SendFunc(func(from string, to []string, msg io.WriterTo) error {
// // Implements you email-sending function, for example by calling
// // an API, or running postfix, etc.
// fmt.Println("From:", from)
// fmt.Println("To:", to)
// return nil
// })
//
// if err := gomail.Send(s, m); err != nil {
// panic(err)
// }
// // Output:
// // From: [email protected]
// // To: [[email protected]]
//}

var m *gomail.Message

Expand Down
11 changes: 6 additions & 5 deletions message_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package gomail

import (
"bytes"
"context"
"encoding/base64"
"io"
"io/ioutil"
Expand Down Expand Up @@ -593,14 +594,14 @@ func TestEmptyHeader(t *testing.T) {
}

func testMessage(t *testing.T, m *Message, bCount int, want *message) {
err := Send(stubSendMail(t, bCount, want), m)
err := sendAll(context.TODO(), stubSendMail(t, bCount, want), m)
if err != nil {
t.Error(err)
}
}

func stubSendMail(t *testing.T, bCount int, want *message) SendFunc {
return func(from string, to []string, m io.WriterTo) error {
func stubSendMail(t *testing.T, bCount int, want *message) sendFunc {
return func(ctx context.Context, from string, to []string, m io.WriterTo) error {
if from != want.from {
t.Fatalf("Invalid from, got %q, want %q", from, want.from)
}
Expand Down Expand Up @@ -717,7 +718,7 @@ func mockCopyFileWithHeader(m *Message, name string, h map[string][]string) (str
}

func BenchmarkFull(b *testing.B) {
discardFunc := SendFunc(func(from string, to []string, m io.WriterTo) error {
discardFunc := sendFunc(func(ctx context.Context, from string, to []string, m io.WriterTo) error {
_, err := m.WriteTo(ioutil.Discard)
return err
})
Expand All @@ -737,7 +738,7 @@ func BenchmarkFull(b *testing.B) {
m.Attach(mockCopyFile("benchmark.txt"))
m.Embed(mockCopyFile("benchmark.jpg"))

if err := Send(discardFunc, m); err != nil {
if err := sendAll(context.TODO(), discardFunc, m); err != nil {
panic(err)
}
m.Reset()
Expand Down
40 changes: 20 additions & 20 deletions send.go
Original file line number Diff line number Diff line change
@@ -1,49 +1,49 @@
package gomail

import (
"context"
"errors"
"fmt"
"io"
"net/mail"
)

// Sender is the interface that wraps the Send method.
// sender is the interface that wraps the Send method.
//
// Send sends an email to the given addresses.
type Sender interface {
Send(from string, to []string, msg io.WriterTo) error
type sender interface {
Send(ctx context.Context, from string, to []string, msg io.WriterTo) error
}

// SendCloser is the interface that groups the Send and Close methods.
type SendCloser interface {
Sender
// sendCloser is the interface that groups the Send and Close methods.
type sendCloser interface {
sender
Close() error
}

// A SendFunc is a function that sends emails to the given addresses.
// A sendFunc is a function that sends emails to the given addresses.
//
// The SendFunc type is an adapter to allow the use of ordinary functions as
// email senders. If f is a function with the appropriate signature, SendFunc(f)
// is a Sender object that calls f.
type SendFunc func(from string, to []string, msg io.WriterTo) error

// Send calls f(from, to, msg).
func (f SendFunc) Send(from string, to []string, msg io.WriterTo) error {
return f(from, to, msg)
// The sendFunc type is an adapter to allow the use of ordinary functions as
// email senders. If f is a function with the appropriate signature, sendFunc(f)
// is a sender object that calls f.
type sendFunc func(ctx context.Context, from string, to []string, msg io.WriterTo) error

func (f sendFunc) Send(ctx context.Context, from string, to []string, msg io.WriterTo) error {
return f(ctx, from, to, msg)
}

// Send sends emails using the given Sender.
func Send(s Sender, msg ...*Message) error {
// sendAll sends emails using the given sender.
func sendAll(ctx context.Context, s sender, msg ...*Message) error {
for i, m := range msg {
if err := send(s, m); err != nil {
if err := send(ctx, s, m); err != nil {
return fmt.Errorf("gomail: could not send email %d: %v", i+1, err)
}
}

return nil
}

func send(s Sender, m *Message) error {
func send(ctx context.Context, s sender, m *Message) error {
from, err := m.getFrom()
if err != nil {
return err
Expand All @@ -54,7 +54,7 @@ func send(s Sender, m *Message) error {
return err
}

if err := s.Send(from, to, m); err != nil {
if err := s.Send(ctx, from, to, m); err != nil {
return err
}

Expand Down
11 changes: 6 additions & 5 deletions send_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package gomail

import (
"bytes"
"context"
"io"
"reflect"
"testing"
Expand All @@ -22,10 +23,10 @@ const (
testBody
)

type mockSender SendFunc
type mockSender sendFunc

func (s mockSender) Send(from string, to []string, msg io.WriterTo) error {
return s(from, to, msg)
func (s mockSender) Send(ctx context.Context, from string, to []string, msg io.WriterTo) error {
return s(ctx, from, to, msg)
}

type mockSendCloser struct {
Expand All @@ -45,7 +46,7 @@ func TestSend(t *testing.T) {
return nil
},
}
if err := Send(s, getTestMessage()); err != nil {
if err := sendAll(context.TODO(), s, getTestMessage()); err != nil {
t.Errorf("Send(): %v", err)
}
}
Expand All @@ -60,7 +61,7 @@ func getTestMessage() *Message {
}

func stubSend(t *testing.T, wantFrom string, wantTo []string, wantBody string) mockSender {
return func(from string, to []string, msg io.WriterTo) error {
return func(ctx context.Context, from string, to []string, msg io.WriterTo) error {
if from != wantFrom {
t.Errorf("invalid from, got %q, want %q", from, wantFrom)
}
Expand Down
Loading

0 comments on commit 7f421f5

Please sign in to comment.