~emersion/public-inbox

go-maildir: Implement Copy function v2 PROPOSED

Ben Burwell: 1
 Implement Copy function

 2 files changed, 101 insertions(+), 0 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~emersion/public-inbox/patches/7147/mbox | git am -3
Learn more about email & git

[PATCH go-maildir v2] Implement Copy function Export this patch

---
Since v1: split out the copyToTmp function which encapsulates all of the
file opening/reading/writing/closing. A nice side effect of having a
function which only does that is that we no longer need to close the
files before the end of the function and can simply let the deferred
calls to to rc.Close and wc.Close do the work.

 maildir.go      | 48 ++++++++++++++++++++++++++++++++++++++++++++
 maildir_test.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 101 insertions(+)

diff --git a/maildir.go b/maildir.go
index 8cab16d..8b42149 100644
--- a/maildir.go
+++ b/maildir.go
@@ -384,6 +384,54 @@ func (d Dir) Move(target Dir, key string) error {
 	return os.Rename(path, filepath.Join(string(target), "cur", filepath.Base(path)))
 }
 
+// Copy copies the message with key from this Maildir to the target, preserving
+// its flags, returning the newly generated key for the target maildir or an
+// error.
+func (d Dir) Copy(target Dir, key string) (string, error) {
+	flags, err := d.Flags(key)
+	if err != nil {
+		return "", err
+	}
+	targetKey, err := d.copyToTmp(target, key)
+	if err != nil {
+		return "", err
+	}
+	tmpfile := filepath.Join(string(target), "tmp", targetKey)
+	curfile := filepath.Join(string(target), "cur", targetKey+"2,")
+	if err = os.Rename(tmpfile, curfile); err != nil {
+		return "", err
+	}
+	if err = target.SetFlags(targetKey, flags); err != nil {
+		return "", err
+	}
+	return targetKey, nil
+}
+
+// copyToTmp copies the message with key from d into a file in the target
+// maildir's tmp directory with a new key, returning the newly generated key or
+// an error.
+func (d Dir) copyToTmp(target Dir, key string) (string, error) {
+	rc, err := d.Open(key)
+	if err != nil {
+		return "", err
+	}
+	defer rc.Close()
+	targetKey, err := newKey()
+	if err != nil {
+		return "", err
+	}
+	tmpfile := filepath.Join(string(target), "tmp", targetKey)
+	wc, err := os.OpenFile(tmpfile, os.O_CREATE|os.O_WRONLY, 0600)
+	if err != nil {
+		return "", err
+	}
+	defer wc.Close()
+	if _, err = io.Copy(wc, rc); err != nil {
+		return "", err
+	}
+	return targetKey, nil
+}
+
 // Remove removes the actual file behind this message.
 func (d Dir) Remove(key string) error {
 	f, err := d.Filename(key)
diff --git a/maildir_test.go b/maildir_test.go
index 3164df3..ee79f2f 100644
--- a/maildir_test.go
+++ b/maildir_test.go
@@ -203,6 +203,59 @@ func TestMove(t *testing.T) {
 
 }
 
+func TestCopy(t *testing.T) {
+	t.Parallel()
+	var d1 Dir = "test_copy1"
+	err := d1.Create()
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer cleanup(t.Error, d1)
+	var d2 Dir = "test_copy2"
+	err = d2.Create()
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer cleanup(t.Error, d2)
+	const msg = "a moving message"
+	makeDelivery(t.Fatal, d1, msg)
+	keys, err := d1.Unseen()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if err = d1.SetFlags(keys[0], []Flag{FlagSeen}); err != nil {
+		t.Fatal(err)
+	}
+	key2, err := d1.Copy(d2, keys[0])
+	if err != nil {
+		t.Fatal(err)
+	}
+	path, err := d1.Filename(keys[0])
+	if err != nil {
+		t.Fatal(err)
+	}
+	if cat(t, path) != msg {
+		t.Error("original content has changed")
+	}
+	path, err = d2.Filename(key2)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if cat(t, path) != msg {
+		t.Error("target content doesn't match source")
+	}
+	flags, err := d2.Flags(key2)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(flags) != 1 {
+		t.Fatal("no flags on target")
+	}
+	if flags[0] != FlagSeen {
+		t.Error("seen flag not present on target")
+	}
+}
+
 func BenchmarkFilename(b *testing.B) {
 	// set up test maildir
 	d := Dir("benchmark_filename")
-- 
2.22.0
Pushed:

To github.com:emersion/go-maildir.git
   81c527bce6e9..941194b0ac70  master -> master

Thanks!
View this thread in the archives