NewMailDir and parse MailFile from string

This commit is contained in:
Blallo 2021-01-17 23:38:42 +01:00
parent 5a199eec6b
commit b74764b71a
No known key found for this signature in database
GPG key ID: 0CBE577C9B72DC3F
2 changed files with 192 additions and 8 deletions

View file

@ -2,15 +2,24 @@ package fs
import ( import (
"crypto/md5" "crypto/md5"
"errors"
"fmt" "fmt"
"os"
"path"
"sort" "sort"
"strconv"
"strings" "strings"
"time" "time"
) )
var ErrPathIsNotDirectory = errors.New("the path is not a directory")
var ErrMalformedName = errors.New("file name is not recognised")
var ErrUnknownFlag = errors.New("flag not known")
type Flag string type Flag string
func cat(f []Flag) string { func cat(f []Flag) string {
sort.Slice(f, func(i, j int) bool { return f[i] < f[j] })
var list []string var list []string
for _, el := range f { for _, el := range f {
list = append(list, string(el)) list = append(list, string(el))
@ -19,13 +28,62 @@ func cat(f []Flag) string {
} }
const ( const (
Seen Flag = "S" FlagSeen Flag = "S"
Answered = "R" FlagAnswered = "R"
Flagged = "F" FlagFlagged = "F"
Deleted = "T" FlagDeleted = "T"
Draft = "D" FlagDraft = "D"
FlagUnknown = ""
) )
func flagFromChar(char rune) (Flag, error) {
switch string(char) {
case "S":
return FlagSeen, nil
case "R":
return FlagAnswered, nil
case "F":
return FlagFlagged, nil
case "T":
return FlagDeleted, nil
case "D":
return FlagDraft, nil
default:
return FlagUnknown, ErrUnknownFlag
}
}
func flagsFromString(flags string) ([]Flag, error) {
result := []Flag{}
knownFlags := map[Flag]bool{
FlagSeen: true,
FlagAnswered: true,
FlagFlagged: true,
FlagDeleted: true,
FlagDraft: true,
}
if flags == "" {
return result, nil
}
for _, f := range flags {
if flag, err := flagFromChar(f); err != nil {
return []Flag{}, err
} else {
if knownFlags[flag] {
result = append(result, flag)
knownFlags[flag] = false
} else {
return []Flag{}, ErrMalformedName
}
}
}
sort.Slice(result, func(i, j int) bool { return result[i] < result[j] })
return result, nil
}
// MailFile holds the information needed to format the name of the file // MailFile holds the information needed to format the name of the file
// containing a single email. The format is: // containing a single email. The format is:
// `<%d_%d.%d.%s>,U=<%d>,FMD5=<%s>:2,<FLAGS>` // `<%d_%d.%d.%s>,U=<%d>,FMD5=<%s>:2,<FLAGS>`
@ -79,6 +137,91 @@ func (m *MailFile) String() string {
) )
} }
func NewMailFile(name string) (*MailFile, error) {
mailFile := &MailFile{}
parts := strings.Split(name, ",")
if len(parts) != 4 {
return mailFile, ErrMalformedName
}
sub1 := strings.Split(parts[0], ".")
if len(sub1) != 3 {
return mailFile, ErrMalformedName
}
sub2 := strings.Split(sub1[0], "_")
if len(sub2) != 2 {
return mailFile, ErrMalformedName
}
unix, err := strconv.ParseInt(sub2[0], 10, 64)
if err != nil {
return mailFile, err
}
mailFile.timestamp = time.Unix(unix, 0)
progressive, err := strconv.ParseInt(sub2[1], 10, 32)
if err != nil {
return mailFile, err
}
mailFile.progressive = int(progressive)
pid, err := strconv.ParseInt(sub1[1], 10, 32)
if err != nil {
return mailFile, err
}
mailFile.pid = int(pid)
mailFile.hostname = sub1[2]
sub3 := strings.Split(parts[1], "=")
if len(sub3) != 2 || sub3[0] != "U" {
return mailFile, ErrMalformedName
}
uid, err := strconv.ParseInt(sub3[1], 10, 32)
if err != nil {
return mailFile, err
}
mailFile.uid = int(uid)
sub4 := strings.Split(parts[2], "=")
if len(sub4) != 2 || sub4[0] != "FMD5" {
return mailFile, ErrMalformedName
}
sub5 := strings.Split(sub4[1], ":")
if len(sub5) != 2 || sub5[1] != "2" {
return mailFile, ErrMalformedName
}
mailFile.md5 = sub5[0]
flags, err := flagsFromString(parts[3])
if err != nil {
return mailFile, err
}
mailFile.flags = flags
return mailFile, nil
}
type MailDir struct { type MailDir struct {
basePath string basePath string
name string
MailFiles []MailFile
}
func NewMailDir(name, basePath string) (*MailDir, error) {
mailDirPath := path.Join(basePath, name)
mailDir := &MailDir{basePath: basePath, name: name, MailFiles: []MailFile{}}
info, err := os.Stat(mailDirPath)
if err != nil {
if err == os.ErrNotExist {
err = os.Mkdir(mailDirPath, 0750)
}
}
if !info.IsDir() {
return mailDir, ErrPathIsNotDirectory
}
return mailDir, err
} }

View file

@ -3,10 +3,32 @@ package fs
import ( import (
"fmt" "fmt"
"testing" "testing"
"time"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
) )
func TestFlagsFromString(t *testing.T) {
flags, err := flagsFromString("RTDSF")
if err != nil {
t.Errorf("Flag parsing errored unexpectedly: %s\n", err)
}
expect := []Flag{FlagDraft, FlagFlagged, FlagAnswered, FlagSeen, FlagDeleted}
if !cmp.Equal(flags, expect) {
t.Errorf("Flags mismatch: %s", cmp.Diff(expect, flags))
}
_, err = flagsFromString("RDX")
if err != ErrUnknownFlag {
t.Error("Flag parsing should have errored")
}
_, err = flagsFromString("DDF")
if err != ErrMalformedName {
t.Error("Flag parsing should have errored")
}
}
func TestMailFileString(t *testing.T) { func TestMailFileString(t *testing.T) {
clock := mockClock{} clock := mockClock{}
timestamp := clock.Now() timestamp := clock.Now()
@ -16,7 +38,7 @@ func TestMailFileString(t *testing.T) {
name := "main_account" // known md5sum: f2cf513ad46d4d9b9684103e468803a0 name := "main_account" // known md5sum: f2cf513ad46d4d9b9684103e468803a0
uid := 33243 uid := 33243
mf := &MailFile{timestamp: timestamp, progressive: progressive, pid: pid, hostname: hostname} mf := &MailFile{timestamp: timestamp, progressive: progressive, pid: pid, hostname: hostname}
mf.SetFlags([]Flag{Seen, Answered}) mf.SetFlags([]Flag{FlagSeen, FlagAnswered})
mf.SetMd5FromName(name) mf.SetMd5FromName(name)
mf.SetUid(uid) mf.SetUid(uid)
@ -34,3 +56,22 @@ func TestMailFileString(t *testing.T) {
t.Logf("diff: %s", cmp.Diff(expect, result)) t.Logf("diff: %s", cmp.Diff(expect, result))
} }
} }
func TestNewMailFile(t *testing.T) {
mf, err := NewMailFile("12345_0.1312.myplace,U=666,FMD5=f2cf513ad46d4d9b9684103e468803a0:2,")
if err != nil {
t.Errorf("Unexpected error: %s\n", err)
}
expect := &MailFile{
timestamp: time.Unix(12345, 0),
progressive: 0,
pid: 1312,
hostname: "myplace",
uid: 666,
md5: "f2cf513ad46d4d9b9684103e468803a0",
flags: []Flag{},
}
if !cmp.Equal(mf, expect, cmp.AllowUnexported(MailFile{})) {
t.Errorf("MailFile mismatch: %s", cmp.Diff(expect, mf, cmp.AllowUnexported(MailFile{})))
}
}