aboutsummaryrefslogtreecommitdiff
path: root/lib/format/format.go
diff options
context:
space:
mode:
Diffstat (limited to 'lib/format/format.go')
-rw-r--r--lib/format/format.go263
1 files changed, 263 insertions, 0 deletions
diff --git a/lib/format/format.go b/lib/format/format.go
new file mode 100644
index 0000000..b403f2d
--- /dev/null
+++ b/lib/format/format.go
@@ -0,0 +1,263 @@
+package format
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+ "unicode"
+
+ "git.sr.ht/~sircmpwn/aerc/models"
+)
+
+func ParseMessageFormat(format string, timestampformat string,
+ accountName string, number int, msg *models.MessageInfo) (string,
+ []interface{}, error) {
+ retval := make([]byte, 0, len(format))
+ var args []interface{}
+
+ var c rune
+ for i, ni := 0, 0; i < len(format); {
+ ni = strings.IndexByte(format[i:], '%')
+ if ni < 0 {
+ ni = len(format)
+ retval = append(retval, []byte(format[i:ni])...)
+ break
+ }
+ ni += i + 1
+ // Check for fmt flags
+ if ni == len(format) {
+ goto handle_end_error
+ }
+ c = rune(format[ni])
+ if c == '+' || c == '-' || c == '#' || c == ' ' || c == '0' {
+ ni++
+ }
+
+ // Check for precision and width
+ if ni == len(format) {
+ goto handle_end_error
+ }
+ c = rune(format[ni])
+ for unicode.IsDigit(c) {
+ ni++
+ c = rune(format[ni])
+ }
+ if c == '.' {
+ ni++
+ c = rune(format[ni])
+ for unicode.IsDigit(c) {
+ ni++
+ c = rune(format[ni])
+ }
+ }
+
+ retval = append(retval, []byte(format[i:ni])...)
+ // Get final format verb
+ if ni == len(format) {
+ goto handle_end_error
+ }
+ c = rune(format[ni])
+ switch c {
+ case '%':
+ retval = append(retval, '%')
+ case 'a':
+ if len(msg.Envelope.From) == 0 {
+ return "", nil,
+ errors.New("found no address for sender")
+ }
+ addr := msg.Envelope.From[0]
+ retval = append(retval, 's')
+ args = append(args,
+ fmt.Sprintf("%s@%s", addr.Mailbox, addr.Host))
+ case 'A':
+ var addr *models.Address
+ if len(msg.Envelope.ReplyTo) == 0 {
+ if len(msg.Envelope.From) == 0 {
+ return "", nil,
+ errors.New("found no address for sender or reply-to")
+ } else {
+ addr = msg.Envelope.From[0]
+ }
+ } else {
+ addr = msg.Envelope.ReplyTo[0]
+ }
+ retval = append(retval, 's')
+ args = append(args,
+ fmt.Sprintf("%s@%s", addr.Mailbox, addr.Host))
+ case 'C':
+ retval = append(retval, 'd')
+ args = append(args, number)
+ case 'd':
+ retval = append(retval, 's')
+ args = append(args,
+ msg.InternalDate.Format(timestampformat))
+ case 'D':
+ retval = append(retval, 's')
+ args = append(args,
+ msg.InternalDate.Local().Format(timestampformat))
+ case 'f':
+ if len(msg.Envelope.From) == 0 {
+ return "", nil,
+ errors.New("found no address for sender")
+ }
+ addr := msg.Envelope.From[0].Format()
+ retval = append(retval, 's')
+ args = append(args, addr)
+ case 'F':
+ if len(msg.Envelope.From) == 0 {
+ return "", nil,
+ errors.New("found no address for sender")
+ }
+ addr := msg.Envelope.From[0]
+ // TODO: handle case when sender is current user. Then
+ // use recipient's name
+ var val string
+ if addr.Name != "" {
+ val = addr.Name
+ } else {
+ val = fmt.Sprintf("%s@%s", addr.Mailbox, addr.Host)
+ }
+ retval = append(retval, 's')
+ args = append(args, val)
+
+ case 'i':
+ retval = append(retval, 's')
+ args = append(args, msg.Envelope.MessageId)
+ case 'n':
+ if len(msg.Envelope.From) == 0 {
+ return "", nil,
+ errors.New("found no address for sender")
+ }
+ addr := msg.Envelope.From[0]
+ var val string
+ if addr.Name != "" {
+ val = addr.Name
+ } else {
+ val = fmt.Sprintf("%s@%s", addr.Mailbox, addr.Host)
+ }
+ retval = append(retval, 's')
+ args = append(args, val)
+ case 'r':
+ addrs := models.FormatAddresses(msg.Envelope.To)
+ retval = append(retval, 's')
+ args = append(args, addrs)
+ case 'R':
+ addrs := models.FormatAddresses(msg.Envelope.Cc)
+ retval = append(retval, 's')
+ args = append(args, addrs)
+ case 's':
+ retval = append(retval, 's')
+ args = append(args, msg.Envelope.Subject)
+ case 't':
+ if len(msg.Envelope.To) == 0 {
+ return "", nil,
+ errors.New("found no address for recipient")
+ }
+ addr := msg.Envelope.To[0]
+ retval = append(retval, 's')
+ args = append(args,
+ fmt.Sprintf("%s@%s", addr.Mailbox, addr.Host))
+ case 'T':
+ retval = append(retval, 's')
+ args = append(args, accountName)
+ case 'u':
+ if len(msg.Envelope.From) == 0 {
+ return "", nil,
+ errors.New("found no address for sender")
+ }
+ addr := msg.Envelope.From[0]
+ retval = append(retval, 's')
+ args = append(args, addr.Mailbox)
+ case 'v':
+ if len(msg.Envelope.From) == 0 {
+ return "", nil,
+ errors.New("found no address for sender")
+ }
+ addr := msg.Envelope.From[0]
+ // check if message is from current user
+ if addr.Name != "" {
+ retval = append(retval, 's')
+ args = append(args,
+ strings.Split(addr.Name, " ")[0])
+ }
+ case 'Z':
+ // calculate all flags
+ var readReplyFlag = ""
+ var delFlag = ""
+ var flaggedFlag = ""
+ seen := false
+ recent := false
+ answered := false
+ for _, flag := range msg.Flags {
+ if flag == models.SeenFlag {
+ seen = true
+ } else if flag == models.RecentFlag {
+ recent = true
+ } else if flag == models.AnsweredFlag {
+ answered = true
+ }
+ if flag == models.DeletedFlag {
+ delFlag = "D"
+ // TODO: check if attachments
+ }
+ if flag == models.FlaggedFlag {
+ flaggedFlag = "!"
+ }
+ // TODO: check gpg stuff
+ }
+ if seen {
+ if answered {
+ readReplyFlag = "r" // message has been replied to
+ }
+ } else {
+ if recent {
+ readReplyFlag = "N" // message is new
+ } else {
+ readReplyFlag = "O" // message is old
+ }
+ }
+ retval = append(retval, '3', 's')
+ args = append(args, readReplyFlag+delFlag+flaggedFlag)
+
+ // Move the below cases to proper alphabetical positions once
+ // implemented
+ case 'l':
+ // TODO: number of lines in the message
+ retval = append(retval, 'd')
+ args = append(args, msg.Size)
+ case 'e':
+ // TODO: current message number in thread
+ fallthrough
+ case 'E':
+ // TODO: number of messages in current thread
+ fallthrough
+ case 'H':
+ // TODO: spam attribute(s) of this message
+ fallthrough
+ case 'L':
+ // TODO:
+ fallthrough
+ case 'X':
+ // TODO: number of attachments
+ fallthrough
+ case 'y':
+ // TODO: X-Label field
+ fallthrough
+ case 'Y':
+ // TODO: X-Label field and some other constraints
+ fallthrough
+ default:
+ // Just ignore it and print as is
+ // so %k in index format becomes %%k to Printf
+ retval = append(retval, '%')
+ retval = append(retval, byte(c))
+ }
+ i = ni + 1
+ }
+
+ return string(retval), args, nil
+
+handle_end_error:
+ return "", nil,
+ errors.New("reached end of string while parsing message format")
+}