Browse Source

Add stub fs module with maildir utils

Blallo 3 years ago
parent
commit
fa972455b3
3 changed files with 222 additions and 0 deletions
  1. 62 0
      fs/clock.go
  2. 125 0
      fs/maildir.go
  3. 35 0
      fs/maildir_test.go

+ 62 - 0
fs/clock.go

@@ -0,0 +1,62 @@
+package fs
+
+import (
+	"time"
+)
+
+var now time.Time
+
+func init() {
+	now = time.Now()
+}
+
+type ticker interface {
+	getC() <-chan time.Time
+}
+
+type clock interface {
+	Now() time.Time
+	Ticker() ticker
+}
+
+type realTicker time.Ticker
+
+func (rt realTicker) getC() <-chan time.Time {
+	return rt.C
+}
+
+type realClock struct{}
+
+func (rc realClock) Now() time.Time {
+	return time.Now()
+}
+
+func (rc realClock) Ticker() ticker {
+	ticker := time.NewTicker(time.Second)
+	return realTicker(*ticker)
+}
+
+type mockClock struct{}
+
+func (mc mockClock) Now() time.Time {
+	return now
+}
+
+func (mc mockClock) Ticker() ticker {
+	return newMockTicker()
+}
+
+type mockTicker struct {
+	c <-chan time.Time
+}
+
+func (mc mockTicker) getC() <-chan time.Time {
+	return mc.c
+}
+
+func newMockTicker() mockTicker {
+	c := make(<-chan time.Time)
+	return mockTicker{
+		c: c,
+	}
+}

+ 125 - 0
fs/maildir.go

@@ -0,0 +1,125 @@
+package fs
+
+import (
+	"fmt"
+	"os"
+	"strings"
+	"time"
+)
+
+type Flag string
+
+func cat(f []Flag) string {
+	var list []string
+	for _, el := range f {
+		list = append(list, string(el))
+	}
+	return strings.Join(list, "")
+}
+
+const (
+	Seen     Flag = "S"
+	Answered      = "R"
+	Flagged       = "F"
+	Deleted       = "T"
+	Draft         = "D"
+)
+
+// MailFile holds the information needed to format the name of the file
+// containing a single email. The format is:
+// `<%d_%d.%d.%s>,U=<%d>,FMD5=<%s>:2,<FLAGS>`
+// (logical groups being enclosed by angle brackets) where:
+//   - `<%d_%d.%d.%s>` is the concatenation of:
+//     * system unix timestamp of arrival time of the message
+//     * a progressive number to distinguish messages arrived at once
+//     * pid of the current process
+//     * hostname of the local machine
+//   - `,U=<%d>` holds the UID of the message as decided by the server
+//   - `,FMD5=<%s>:2` holds the md5sum of the name of the current mailbox
+//   - `,<FLAGS>` carries the flags active on the message
+type MailFile struct {
+	timestamp   time.Time
+	progressive int
+	pid         int
+	hostname    string
+	uid         int
+	md5         string
+	flags       []Flag
+}
+
+func (m *MailFile) SetUid(uid int) {
+	m.uid = uid
+}
+
+func (m *MailFile) SetMd5(md5 string) {
+	m.md5 = md5
+}
+
+func (m *MailFile) SetFlags(flags []Flag) {
+	m.flags = flags
+}
+
+func (m *MailFile) String() string {
+	return fmt.Sprintf(
+		"%d_%d.%d.%s,U=%d,FMD5=%s:2,%s",
+		m.timestamp.Unix(),
+		m.progressive,
+		m.pid,
+		m.hostname,
+		m.uid,
+		m.md5,
+		cat(m.flags),
+	)
+}
+
+type MailFileRequest struct {
+	respCh chan MailFile
+}
+
+func NewMailFileRequest() MailFileRequest {
+	ch := make(chan MailFile)
+	return MailFileRequest{respCh: ch}
+}
+
+func (r MailFileRequest) Response() MailFile {
+	return <-r.respCh
+}
+
+func NewOracle() chan MailFileRequest {
+	r := realClock{}
+	return newOracle(r)
+}
+
+func newOracle(c clock) chan MailFileRequest {
+	reqCh := make(chan MailFileRequest, 10)
+	go spawnOracle(reqCh, c)
+	return reqCh
+}
+
+func spawnOracle(reqCh <-chan MailFileRequest, c clock) {
+	count := 0
+	C := c.Ticker().getC()
+	pid := os.Getpid()
+	hostname, err := os.Hostname()
+	if err != nil {
+		hostname = "MISSING"
+	}
+	for {
+		select {
+		case <-C:
+			count = 0
+		case req := <-reqCh:
+			req.respCh <- MailFile{
+				timestamp:   c.Now(),
+				progressive: count,
+				pid:         pid,
+				hostname:    hostname,
+			}
+			count += 1
+		}
+	}
+}
+
+type MailDir struct {
+	basePath string
+}

+ 35 - 0
fs/maildir_test.go

@@ -0,0 +1,35 @@
+package fs
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+)
+
+func TestMailFileString(t *testing.T) {
+	var message_nums = []int{0, 1, 2, 3, 4}
+	var results []MailFile
+	clock := mockClock{}
+	oracle := newOracle(clock)
+	for range message_nums {
+		req := NewMailFileRequest()
+		oracle <- req
+		results = append(results, req.Response())
+	}
+	for i := range message_nums[1:] {
+		timestamp_c := results[0].timestamp != results[i].timestamp
+		pid_c := results[0].pid != results[i].pid
+		hostname_c := results[0].hostname != results[i].hostname
+
+		if timestamp_c || pid_c || hostname_c {
+			t.Logf("timestamp_c: %+v\n", timestamp_c)
+			t.Logf("pid_c: %+v\n", pid_c)
+			t.Logf("hostname_c: %+v\n", hostname_c)
+			t.Errorf("Mismatching timestamps, %+v\n", cmp.Diff(results[0], results[i], cmp.AllowUnexported(MailFile{})))
+		}
+
+		if results[i].progressive != i {
+			t.Errorf("Unexpected progressive -> have: %d | expect: %d\n", results[i].progressive, i)
+		}
+	}
+}