mirror of
https://github.com/wneessen/go-mail.git
synced 2024-11-24 22:50:49 +01:00
Merge pull request #30 from wneessen/29-embedfs
Closes #29: Allow attaching/embedding from embed.FS
This commit is contained in:
commit
db2d581594
2 changed files with 143 additions and 0 deletions
51
msg.go
51
msg.go
|
@ -7,6 +7,7 @@ package mail
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"embed"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
ht "html/template"
|
ht "html/template"
|
||||||
|
@ -539,6 +540,19 @@ func (m *Msg) AttachTextTemplate(n string, t *tt.Template, d interface{}, o ...F
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AttachFromEmbedFS adds an attachment File from an embed.FS to the Msg
|
||||||
|
func (m *Msg) AttachFromEmbedFS(n string, f *embed.FS, o ...FileOption) error {
|
||||||
|
if f == nil {
|
||||||
|
return fmt.Errorf("embed.FS must not be nil")
|
||||||
|
}
|
||||||
|
ef, err := fileFromEmbedFS(n, f)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.attachments = m.appendFile(m.attachments, ef, o...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// EmbedFile adds an embedded File to the Msg
|
// EmbedFile adds an embedded File to the Msg
|
||||||
func (m *Msg) EmbedFile(n string, o ...FileOption) {
|
func (m *Msg) EmbedFile(n string, o ...FileOption) {
|
||||||
f := fileFromFS(n)
|
f := fileFromFS(n)
|
||||||
|
@ -574,6 +588,19 @@ func (m *Msg) EmbedTextTemplate(n string, t *tt.Template, d interface{}, o ...Fi
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EmbedFromEmbedFS adds an embedded File from an embed.FS to the Msg
|
||||||
|
func (m *Msg) EmbedFromEmbedFS(n string, f *embed.FS, o ...FileOption) error {
|
||||||
|
if f == nil {
|
||||||
|
return fmt.Errorf("embed.FS must not be nil")
|
||||||
|
}
|
||||||
|
ef, err := fileFromEmbedFS(n, f)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.embeds = m.appendFile(m.embeds, ef, o...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Reset resets all headers, body parts and attachments/embeds of the Msg
|
// Reset resets all headers, body parts and attachments/embeds of the Msg
|
||||||
// It leaves already set encodings, charsets, boundaries, etc. as is
|
// It leaves already set encodings, charsets, boundaries, etc. as is
|
||||||
func (m *Msg) Reset() {
|
func (m *Msg) Reset() {
|
||||||
|
@ -767,6 +794,30 @@ func (m *Msg) addDefaultHeader() {
|
||||||
m.SetHeader(HeaderMIMEVersion, string(m.mimever))
|
m.SetHeader(HeaderMIMEVersion, string(m.mimever))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fileFromEmbedFS returns a File pointer from a given file in the provided embed.FS
|
||||||
|
func fileFromEmbedFS(n string, f *embed.FS) (*File, error) {
|
||||||
|
_, err := f.Open(n)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open file from embed.FS: %w", err)
|
||||||
|
}
|
||||||
|
return &File{
|
||||||
|
Name: filepath.Base(n),
|
||||||
|
Header: make(map[string][]string),
|
||||||
|
Writer: func(w io.Writer) (int64, error) {
|
||||||
|
h, err := f.Open(n)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
nb, err := io.Copy(w, h)
|
||||||
|
if err != nil {
|
||||||
|
_ = h.Close()
|
||||||
|
return nb, fmt.Errorf("failed to copy file to io.Writer: %w", err)
|
||||||
|
}
|
||||||
|
return nb, h.Close()
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// fileFromFS returns a File pointer from a given file in the system's file system
|
// fileFromFS returns a File pointer from a given file in the system's file system
|
||||||
func fileFromFS(n string) *File {
|
func fileFromFS(n string) *File {
|
||||||
_, err := os.Stat(n)
|
_, err := os.Stat(n)
|
||||||
|
|
92
msg_test.go
92
msg_test.go
|
@ -7,6 +7,7 @@ package mail
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
htpl "html/template"
|
htpl "html/template"
|
||||||
"io"
|
"io"
|
||||||
|
@ -18,6 +19,9 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:embed README.md
|
||||||
|
var efs embed.FS
|
||||||
|
|
||||||
// TestNewMsg tests the NewMsg method
|
// TestNewMsg tests the NewMsg method
|
||||||
func TestNewMsg(t *testing.T) {
|
func TestNewMsg(t *testing.T) {
|
||||||
m := NewMsg()
|
m := NewMsg()
|
||||||
|
@ -1029,6 +1033,50 @@ func TestMsg_AttachFile(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestMsg_AttachFromEmbedFS tests the Msg.AttachFromEmbedFS and the WithFilename FileOption method
|
||||||
|
func TestMsg_AttachFromEmbedFS(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
file string
|
||||||
|
fn string
|
||||||
|
sf bool
|
||||||
|
}{
|
||||||
|
{"File: README.md", "README.md", "README.md", false},
|
||||||
|
{"File: nonexisting", "", "invalid.file", true},
|
||||||
|
}
|
||||||
|
m := NewMsg()
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := m.AttachFromEmbedFS(tt.file, &efs, WithFileName(tt.fn)); err != nil && !tt.sf {
|
||||||
|
t.Errorf("AttachFromEmbedFS() failed: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(m.attachments) != 1 && !tt.sf {
|
||||||
|
t.Errorf("AttachFile() failed. Number of attachments expected: %d, got: %d", 1,
|
||||||
|
len(m.attachments))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !tt.sf {
|
||||||
|
file := m.attachments[0]
|
||||||
|
if file == nil {
|
||||||
|
t.Errorf("AttachFile() failed. Attachment file pointer is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if file.Name != tt.fn {
|
||||||
|
t.Errorf("AttachFile() failed. Filename of attachment expected: %s, got: %s", tt.fn,
|
||||||
|
file.Name)
|
||||||
|
}
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
if _, err := file.Writer(&buf); err != nil {
|
||||||
|
t.Errorf("failed to execute WriterFunc: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.Reset()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestMsg_AttachFileBrokenFunc tests WriterFunc of the Msg.AttachFile method
|
// TestMsg_AttachFileBrokenFunc tests WriterFunc of the Msg.AttachFile method
|
||||||
func TestMsg_AttachFileBrokenFunc(t *testing.T) {
|
func TestMsg_AttachFileBrokenFunc(t *testing.T) {
|
||||||
m := NewMsg()
|
m := NewMsg()
|
||||||
|
@ -1125,6 +1173,50 @@ func TestMsg_EmbedFile(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestMsg_EmbedFromEmbedFS tests the Msg.EmbedFromEmbedFS and the WithFilename FileOption method
|
||||||
|
func TestMsg_EmbedFromEmbedFS(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
file string
|
||||||
|
fn string
|
||||||
|
sf bool
|
||||||
|
}{
|
||||||
|
{"File: README.md", "README.md", "README.md", false},
|
||||||
|
{"File: nonexisting", "", "invalid.file", true},
|
||||||
|
}
|
||||||
|
m := NewMsg()
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := m.EmbedFromEmbedFS(tt.file, &efs, WithFileName(tt.fn)); err != nil && !tt.sf {
|
||||||
|
t.Errorf("EmbedFromEmbedFS() failed: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(m.embeds) != 1 && !tt.sf {
|
||||||
|
t.Errorf("EmbedFile() failed. Number of embeds expected: %d, got: %d", 1,
|
||||||
|
len(m.embeds))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !tt.sf {
|
||||||
|
file := m.embeds[0]
|
||||||
|
if file == nil {
|
||||||
|
t.Errorf("EmbedFile() failed. Embedded file pointer is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if file.Name != tt.fn {
|
||||||
|
t.Errorf("EmbedFile() failed. Filename of embeds expected: %s, got: %s", tt.fn,
|
||||||
|
file.Name)
|
||||||
|
}
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
if _, err := file.Writer(&buf); err != nil {
|
||||||
|
t.Errorf("failed to execute WriterFunc: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.Reset()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestMsg_EmbedFileBrokenFunc tests WriterFunc of the Msg.EmbedFile method
|
// TestMsg_EmbedFileBrokenFunc tests WriterFunc of the Msg.EmbedFile method
|
||||||
func TestMsg_EmbedFileBrokenFunc(t *testing.T) {
|
func TestMsg_EmbedFileBrokenFunc(t *testing.T) {
|
||||||
m := NewMsg()
|
m := NewMsg()
|
||||||
|
|
Loading…
Reference in a new issue