Restructure mailq package
This commit is contained in:
parent
9e19df62ed
commit
e57c011391
178
mailq/parser.go
178
mailq/parser.go
|
@ -2,192 +2,14 @@ package mailq
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type QEntry struct {
|
|
||||||
Id string
|
|
||||||
Status string
|
|
||||||
Date time.Time
|
|
||||||
Size int
|
|
||||||
Sender string
|
|
||||||
Recipients []string
|
|
||||||
Reason string
|
|
||||||
}
|
|
||||||
|
|
||||||
type MailQ struct {
|
|
||||||
NumTotal int
|
|
||||||
NumActive int
|
|
||||||
NumHold int
|
|
||||||
NumDeferred int
|
|
||||||
Entries []QEntry
|
|
||||||
}
|
|
||||||
|
|
||||||
const SortableDateFormat = "2006-01-02 15:04:05"
|
|
||||||
|
|
||||||
func (m QEntry) String() string {
|
|
||||||
var recipientSuffix string
|
|
||||||
if len(m.Recipients) > 1 {
|
|
||||||
recipientSuffix = ",..."
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("[%s] %s <%s> -> {%d}<%s>%s (%s, %d bytes)", m.Date.Format(SortableDateFormat), m.Id, m.Sender, len(m.Recipients), m.Recipients[0], recipientSuffix, m.Status, m.Size)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m QEntry) MachineReadableString() string {
|
|
||||||
return fmt.Sprintf("%s_%s_%s_%d_%s_%s_%s", m.Id, m.Status, m.Date.Format(SortableDateFormat), m.Size, m.Sender, m.Reason, strings.Join(m.Recipients, ","))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m QEntry) DetailedString() string {
|
|
||||||
var reasonStr string
|
|
||||||
if m.Reason == "" {
|
|
||||||
reasonStr = "-/-"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("Id: %s\nDate: %s\nStatus: %s\nReason: %s\nSize: %d\nSender: %s\nRecipients: %s", m.Id, m.Date.Format(SortableDateFormat), m.Status, reasonStr, m.Size, m.Sender, strings.Join(m.Recipients, ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
type qEntrySortConfig struct {
|
|
||||||
attributes []qEntryAttributeSortConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
type qEntryAttributeSortConfig struct {
|
|
||||||
attribute string
|
|
||||||
order string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSortConfig() qEntrySortConfig {
|
|
||||||
return qEntrySortConfig{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isQEntryAttribute(attributeName string) bool {
|
|
||||||
var isValidAttribute bool
|
|
||||||
switch attributeName {
|
|
||||||
case "Id", "Status", "Date", "Size", "Sender", "Recipients", "Reason":
|
|
||||||
isValidAttribute = true
|
|
||||||
default:
|
|
||||||
isValidAttribute = false
|
|
||||||
}
|
|
||||||
return isValidAttribute
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c qEntrySortConfig) By(attributeName string, order string) qEntrySortConfig {
|
|
||||||
if !isQEntryAttribute(attributeName) {
|
|
||||||
panic(fmt.Sprintf("Invalid sort attribute: '%s' given!", attributeName))
|
|
||||||
}
|
|
||||||
if order != "ASC" && order != "DESC" {
|
|
||||||
panic(fmt.Sprintf("Invalid sort order '%s' given!", order))
|
|
||||||
}
|
|
||||||
newAttributeConfig := qEntryAttributeSortConfig{attribute: attributeName, order: order}
|
|
||||||
c.attributes = append(c.attributes, newAttributeConfig)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (queue MailQ) Sort(config qEntrySortConfig) {
|
|
||||||
if len(config.attributes) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Printf("SortConfig: %q\n", config)
|
|
||||||
sort.Slice(queue.Entries, func(a int, b int) bool {
|
|
||||||
for _, sortBy := range config.attributes {
|
|
||||||
var cmp bool
|
|
||||||
var skip bool
|
|
||||||
// Not sure if these are necessary yet
|
|
||||||
cmp = false
|
|
||||||
skip = false
|
|
||||||
switch sortBy.attribute {
|
|
||||||
case "Id":
|
|
||||||
if queue.Entries[a].Id == queue.Entries[b].Id {
|
|
||||||
skip = true
|
|
||||||
}
|
|
||||||
cmp = queue.Entries[a].Id < queue.Entries[b].Id
|
|
||||||
case "Status":
|
|
||||||
if queue.Entries[a].Status == queue.Entries[b].Status {
|
|
||||||
skip = true
|
|
||||||
}
|
|
||||||
cmp = queue.Entries[a].Status < queue.Entries[b].Status
|
|
||||||
case "Date":
|
|
||||||
if queue.Entries[a].Date.Equal(queue.Entries[b].Date) {
|
|
||||||
skip = true
|
|
||||||
}
|
|
||||||
cmp = queue.Entries[a].Date.Before(queue.Entries[b].Date)
|
|
||||||
case "Size":
|
|
||||||
if queue.Entries[a].Size == queue.Entries[b].Size {
|
|
||||||
skip = true
|
|
||||||
}
|
|
||||||
cmp = queue.Entries[a].Size < queue.Entries[b].Size
|
|
||||||
case "Sender":
|
|
||||||
if queue.Entries[a].Sender == queue.Entries[b].Sender {
|
|
||||||
skip = true
|
|
||||||
}
|
|
||||||
cmp = queue.Entries[a].Sender < queue.Entries[b].Sender
|
|
||||||
case "Recipients":
|
|
||||||
if len(queue.Entries[a].Recipients) == len(queue.Entries[b].Recipients) {
|
|
||||||
skip = true
|
|
||||||
}
|
|
||||||
cmp = len(queue.Entries[a].Recipients) < len(queue.Entries[b].Recipients)
|
|
||||||
case "Reason":
|
|
||||||
if queue.Entries[a].Reason == queue.Entries[b].Reason {
|
|
||||||
skip = true
|
|
||||||
}
|
|
||||||
cmp = queue.Entries[a].Reason < queue.Entries[b].Reason
|
|
||||||
default:
|
|
||||||
// TODO: Handle this error case?
|
|
||||||
}
|
|
||||||
if skip == true {
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
if sortBy.order == "ASC" {
|
|
||||||
return cmp
|
|
||||||
} else if sortBy.order == "DESC" {
|
|
||||||
return !cmp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (queue MailQ) PrintMachineReadable(writer io.Writer) {
|
|
||||||
for _, entry := range queue.Entries {
|
|
||||||
fmt.Fprintf(writer, "%s\n", entry.MachineReadableString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (queue MailQ) PrintHumanReadable(writer io.Writer) {
|
|
||||||
if len(queue.Entries) == 0 {
|
|
||||||
fmt.Fprintf(writer, "Mail queue is empty\n")
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(writer, "%d entries total (%d active, %d deferred, %d on hold)\n\n", len(queue.Entries), queue.NumActive, queue.NumDeferred, queue.NumHold)
|
|
||||||
tabWriter := tabwriter.NewWriter(writer, 2, 2, 1, ' ', tabwriter.TabIndent)
|
|
||||||
fmt.Fprintf(tabWriter, "| %s\t| %s\t| %s\t| %s\t| %s\t| %s\t| %s\t| %s\t| \n", "Date", "Id", "Status", "Size", "Sender", "#", "First Recipient", "Reason")
|
|
||||||
for _, entry := range queue.Entries {
|
|
||||||
_, writeError := fmt.Fprintf(tabWriter, "| %s\t| %s\t| %s\t| %d\t| %s\t| {%d}\t| %s\t| %s\t| \n", entry.Date.Format(SortableDateFormat), entry.Id, entry.Status, entry.Size, entry.Sender, len(entry.Recipients), entry.Recipients[0], entry.Reason)
|
|
||||||
if writeError != nil {
|
|
||||||
// A writeError is expected once the reader was closed
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tabWriter.Flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (queue MailQ) PrintJSON(writer io.Writer) {
|
|
||||||
bytes, err := json.Marshal(queue)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(writer, "Error encoding queue to JSON: %s\n", err.Error())
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(writer, "%s", bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseMailQ(dataSource io.Reader) (MailQ, error) {
|
func ParseMailQ(dataSource io.Reader) (MailQ, error) {
|
||||||
const dateFormat = "2006 Mon Jan _2 15:04:05"
|
const dateFormat = "2006 Mon Jan _2 15:04:05"
|
||||||
var messageIdStart = regexp.MustCompile("^[0-9A-Za-z]+[*!]? ")
|
var messageIdStart = regexp.MustCompile("^[0-9A-Za-z]+[*!]? ")
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
package mailq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
type qEntrySortConfig struct {
|
||||||
|
attributes []qEntryAttributeSortConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type qEntryAttributeSortConfig struct {
|
||||||
|
attribute string
|
||||||
|
order string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSortConfig() qEntrySortConfig {
|
||||||
|
return qEntrySortConfig{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isQEntryAttribute(attributeName string) bool {
|
||||||
|
var isValidAttribute bool
|
||||||
|
switch attributeName {
|
||||||
|
case "Id", "Status", "Date", "Size", "Sender", "Recipients", "Reason":
|
||||||
|
isValidAttribute = true
|
||||||
|
default:
|
||||||
|
isValidAttribute = false
|
||||||
|
}
|
||||||
|
return isValidAttribute
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c qEntrySortConfig) By(attributeName string, order string) qEntrySortConfig {
|
||||||
|
if !isQEntryAttribute(attributeName) {
|
||||||
|
panic(fmt.Sprintf("Invalid sort attribute: '%s' given!", attributeName))
|
||||||
|
}
|
||||||
|
if order != "ASC" && order != "DESC" {
|
||||||
|
panic(fmt.Sprintf("Invalid sort order '%s' given!", order))
|
||||||
|
}
|
||||||
|
newAttributeConfig := qEntryAttributeSortConfig{attribute: attributeName, order: order}
|
||||||
|
c.attributes = append(c.attributes, newAttributeConfig)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue MailQ) Sort(config qEntrySortConfig) {
|
||||||
|
if len(config.attributes) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("SortConfig: %q\n", config)
|
||||||
|
sort.Slice(queue.Entries, func(a int, b int) bool {
|
||||||
|
for _, sortBy := range config.attributes {
|
||||||
|
var cmp bool
|
||||||
|
var skip bool
|
||||||
|
// Not sure if these are necessary yet
|
||||||
|
cmp = false
|
||||||
|
skip = false
|
||||||
|
switch sortBy.attribute {
|
||||||
|
case "Id":
|
||||||
|
if queue.Entries[a].Id == queue.Entries[b].Id {
|
||||||
|
skip = true
|
||||||
|
}
|
||||||
|
cmp = queue.Entries[a].Id < queue.Entries[b].Id
|
||||||
|
case "Status":
|
||||||
|
if queue.Entries[a].Status == queue.Entries[b].Status {
|
||||||
|
skip = true
|
||||||
|
}
|
||||||
|
cmp = queue.Entries[a].Status < queue.Entries[b].Status
|
||||||
|
case "Date":
|
||||||
|
if queue.Entries[a].Date.Equal(queue.Entries[b].Date) {
|
||||||
|
skip = true
|
||||||
|
}
|
||||||
|
cmp = queue.Entries[a].Date.Before(queue.Entries[b].Date)
|
||||||
|
case "Size":
|
||||||
|
if queue.Entries[a].Size == queue.Entries[b].Size {
|
||||||
|
skip = true
|
||||||
|
}
|
||||||
|
cmp = queue.Entries[a].Size < queue.Entries[b].Size
|
||||||
|
case "Sender":
|
||||||
|
if queue.Entries[a].Sender == queue.Entries[b].Sender {
|
||||||
|
skip = true
|
||||||
|
}
|
||||||
|
cmp = queue.Entries[a].Sender < queue.Entries[b].Sender
|
||||||
|
case "Recipients":
|
||||||
|
if len(queue.Entries[a].Recipients) == len(queue.Entries[b].Recipients) {
|
||||||
|
skip = true
|
||||||
|
}
|
||||||
|
cmp = len(queue.Entries[a].Recipients) < len(queue.Entries[b].Recipients)
|
||||||
|
case "Reason":
|
||||||
|
if queue.Entries[a].Reason == queue.Entries[b].Reason {
|
||||||
|
skip = true
|
||||||
|
}
|
||||||
|
cmp = queue.Entries[a].Reason < queue.Entries[b].Reason
|
||||||
|
default:
|
||||||
|
// TODO: Handle this error case?
|
||||||
|
}
|
||||||
|
if skip == true {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
if sortBy.order == "ASC" {
|
||||||
|
return cmp
|
||||||
|
} else if sortBy.order == "DESC" {
|
||||||
|
return !cmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package mailq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"text/tabwriter"
|
||||||
|
)
|
||||||
|
|
||||||
|
const SortableDateFormat = "2006-01-02 15:04:05"
|
||||||
|
|
||||||
|
func (m QEntry) String() string {
|
||||||
|
var recipientSuffix string
|
||||||
|
if len(m.Recipients) > 1 {
|
||||||
|
recipientSuffix = ",..."
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("[%s] %s <%s> -> {%d}<%s>%s (%s, %d bytes)", m.Date.Format(SortableDateFormat), m.Id, m.Sender, len(m.Recipients), m.Recipients[0], recipientSuffix, m.Status, m.Size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m QEntry) MachineReadableString() string {
|
||||||
|
return fmt.Sprintf("%s_%s_%s_%d_%s_%s_%s", m.Id, m.Status, m.Date.Format(SortableDateFormat), m.Size, m.Sender, m.Reason, strings.Join(m.Recipients, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m QEntry) DetailedString() string {
|
||||||
|
var reasonStr string
|
||||||
|
if m.Reason == "" {
|
||||||
|
reasonStr = "-/-"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("Id: %s\nDate: %s\nStatus: %s\nReason: %s\nSize: %d\nSender: %s\nRecipients: %s", m.Id, m.Date.Format(SortableDateFormat), m.Status, reasonStr, m.Size, m.Sender, strings.Join(m.Recipients, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue MailQ) PrintMachineReadable(writer io.Writer) {
|
||||||
|
for _, entry := range queue.Entries {
|
||||||
|
fmt.Fprintf(writer, "%s\n", entry.MachineReadableString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue MailQ) PrintHumanReadable(writer io.Writer) {
|
||||||
|
if len(queue.Entries) == 0 {
|
||||||
|
fmt.Fprintf(writer, "Mail queue is empty\n")
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(writer, "%d entries total (%d active, %d deferred, %d on hold)\n\n", len(queue.Entries), queue.NumActive, queue.NumDeferred, queue.NumHold)
|
||||||
|
tabWriter := tabwriter.NewWriter(writer, 2, 2, 1, ' ', tabwriter.TabIndent)
|
||||||
|
fmt.Fprintf(tabWriter, "| %s\t| %s\t| %s\t| %s\t| %s\t| %s\t| %s\t| %s\t| \n", "Date", "Id", "Status", "Size", "Sender", "#", "First Recipient", "Reason")
|
||||||
|
for _, entry := range queue.Entries {
|
||||||
|
_, writeError := fmt.Fprintf(tabWriter, "| %s\t| %s\t| %s\t| %d\t| %s\t| {%d}\t| %s\t| %s\t| \n", entry.Date.Format(SortableDateFormat), entry.Id, entry.Status, entry.Size, entry.Sender, len(entry.Recipients), entry.Recipients[0], entry.Reason)
|
||||||
|
if writeError != nil {
|
||||||
|
// A writeError is expected once the reader was closed
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tabWriter.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue MailQ) PrintJSON(writer io.Writer) {
|
||||||
|
bytes, err := json.Marshal(queue)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(writer, "Error encoding queue to JSON: %s\n", err.Error())
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(writer, "%s", bytes)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package mailq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QEntry struct {
|
||||||
|
Id string
|
||||||
|
Status string
|
||||||
|
Date time.Time
|
||||||
|
Size int
|
||||||
|
Sender string
|
||||||
|
Recipients []string
|
||||||
|
Reason string
|
||||||
|
}
|
||||||
|
|
||||||
|
type MailQ struct {
|
||||||
|
NumTotal int
|
||||||
|
NumActive int
|
||||||
|
NumHold int
|
||||||
|
NumDeferred int
|
||||||
|
Entries []QEntry
|
||||||
|
}
|
Loading…
Reference in New Issue