Add stub fs module with maildir utils
This commit is contained in:
parent
483f84a443
commit
fa972455b3
3 changed files with 222 additions and 0 deletions
62
fs/clock.go
Normal file
62
fs/clock.go
Normal file
|
@ -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
fs/maildir.go
Normal file
125
fs/maildir.go
Normal file
|
@ -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
fs/maildir_test.go
Normal file
35
fs/maildir_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue