2021-01-29 12:29:23 +01:00
package task
import (
"errors"
"fmt"
"strings"
"git.sr.ht/~ewintr/gte/pkg/mstore"
"github.com/google/uuid"
)
var (
ErrOutdatedTask = errors . New ( "task is outdated" )
)
2021-01-29 17:22:07 +01:00
const (
FOLDER_INBOX = "INBOX"
FOLDER_NEW = "New"
2021-01-29 12:29:23 +01:00
2021-01-29 18:10:06 +01:00
QUOTE_PREFIX = ">"
PREVIOUS_SEPARATOR = "Previous version:"
FIELD_SEPARATOR = ":"
FIELD_ID = "id"
FIELD_ACTION = "action"
2021-01-29 17:22:07 +01:00
)
2021-01-29 12:29:23 +01:00
2021-01-29 17:22:07 +01:00
// Task reperesents a task based on the data stored in a message
2021-01-29 12:29:23 +01:00
type Task struct {
2021-01-29 17:22:07 +01:00
// Id is a UUID that gets carried over when a new message is constructed
Id string
// Folder is the same name as the mstore folder
Folder string
Action string
Due Date
Message * mstore . Message
// Current indicates whether the task represents an existing message in the mstore
Current bool
// Dirty indicates whether the task contains updates not present in the message
Dirty bool
2021-01-29 12:29:23 +01:00
}
2021-01-29 17:22:07 +01:00
// New constructs a Task based on an mstore.Message.
//
// The data in the message is stored as key: value pairs, one per line. The line can start with quoting marks.
// The subject line also contains values in the format "date - project - action".
// Keys that exist more than once are merged. The one that appears first in the body takes precedence. A value present in the Body takes precedence over one in the subject.
// This enables updating a task by forwarding a topposted message whith new values for fields that the user wants to update.
func New ( msg * mstore . Message ) * Task {
dirty := false
2021-01-29 17:48:22 +01:00
id , d := FieldFromBody ( FIELD_ID , msg . Body )
2021-01-29 12:29:23 +01:00
if id == "" {
id = uuid . New ( ) . String ( )
2021-01-29 17:22:07 +01:00
dirty = true
2021-01-29 12:29:23 +01:00
}
2021-01-29 17:48:22 +01:00
if d {
dirty = true
}
2021-01-29 12:29:23 +01:00
2021-01-29 17:48:22 +01:00
action , d := FieldFromBody ( FIELD_ACTION , msg . Body )
2021-01-29 12:29:23 +01:00
if action == "" {
2021-01-29 17:22:07 +01:00
action = FieldFromSubject ( FIELD_ACTION , msg . Subject )
if action != "" {
dirty = true
}
2021-01-29 12:29:23 +01:00
}
2021-01-29 17:48:22 +01:00
if d {
dirty = true
}
2021-01-29 12:29:23 +01:00
folder := msg . Folder
2021-01-29 17:22:07 +01:00
if folder == FOLDER_INBOX {
folder = FOLDER_NEW
dirty = true
2021-01-29 12:29:23 +01:00
}
return & Task {
2021-01-29 17:22:07 +01:00
Id : id ,
Action : action ,
Folder : folder ,
Message : msg ,
Current : true ,
Dirty : dirty ,
2021-01-29 12:29:23 +01:00
}
}
2021-01-29 17:22:07 +01:00
func ( t * Task ) FormatSubject ( ) string {
return t . Action
}
2021-01-29 12:29:23 +01:00
2021-01-29 17:22:07 +01:00
func ( t * Task ) FormatBody ( ) string {
body := fmt . Sprintf ( "\n" )
order := [ ] string { FIELD_ID , FIELD_ACTION }
fields := map [ string ] string {
FIELD_ID : t . Id ,
FIELD_ACTION : t . Action ,
2021-01-29 12:29:23 +01:00
}
2021-01-29 17:22:07 +01:00
keyLen := 0
for _ , f := range order {
if len ( f ) > keyLen {
keyLen = len ( f )
}
2021-01-29 12:29:23 +01:00
}
2021-01-29 17:22:07 +01:00
for _ , f := range order {
key := f + FIELD_SEPARATOR
for i := len ( key ) ; i <= keyLen ; i ++ {
key += " "
}
line := strings . TrimSpace ( fmt . Sprintf ( "%s %s" , key , fields [ f ] ) )
body += fmt . Sprintf ( "%s\n" , line )
2021-01-29 12:29:23 +01:00
}
2021-01-29 18:10:06 +01:00
if t . Message != nil {
body += fmt . Sprintf ( "\nPrevious version:\n\n%s\n" , t . Message . Body )
}
2021-01-29 12:29:23 +01:00
return body
}
2021-01-29 17:48:22 +01:00
func FieldFromBody ( field , body string ) ( string , bool ) {
value := ""
dirty := false
2021-01-29 12:29:23 +01:00
lines := strings . Split ( body , "\n" )
for _ , line := range lines {
2021-01-29 18:10:06 +01:00
line = strings . TrimSpace ( strings . TrimPrefix ( line , QUOTE_PREFIX ) )
if line == PREVIOUS_SEPARATOR {
return value , dirty
}
2021-01-29 17:22:07 +01:00
parts := strings . SplitN ( line , FIELD_SEPARATOR , 2 )
2021-01-29 12:29:23 +01:00
if len ( parts ) < 2 {
continue
}
2021-01-29 17:48:22 +01:00
2021-01-29 18:10:06 +01:00
fieldName := strings . ToLower ( strings . TrimSpace ( parts [ 0 ] ) )
2021-01-29 17:48:22 +01:00
if fieldName == field {
if value == "" {
value = strings . TrimSpace ( parts [ 1 ] )
} else {
dirty = true
}
2021-01-29 12:29:23 +01:00
}
}
2021-01-29 17:48:22 +01:00
return value , dirty
2021-01-29 12:29:23 +01:00
}
func FieldFromSubject ( field , subject string ) string {
2021-01-29 17:22:07 +01:00
if field == FIELD_ACTION {
2021-01-29 12:29:23 +01:00
return strings . ToLower ( subject )
}
return ""
}