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 17:22:07 +01:00
FIELD_SEPARATOR = ":"
FIELD_ID = "id"
FIELD_ACTION = "action"
)
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
id := 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:22:07 +01:00
action := 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
}
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
}
return body
}
func FieldFromBody ( field , body string ) string {
lines := strings . Split ( body , "\n" )
for _ , line := range lines {
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:22:07 +01:00
if strings . ToLower ( strings . TrimSpace ( parts [ 0 ] ) ) == field {
2021-01-29 12:29:23 +01:00
return strings . TrimSpace ( parts [ 1 ] )
}
}
return ""
}
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 ""
}