-
Notifications
You must be signed in to change notification settings - Fork 0
/
task.go
126 lines (98 loc) · 3.14 KB
/
task.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package main
import (
"crypto/sha1"
"fmt"
"strings"
"time"
)
// Task represents text as content a creation date and a date indicating when
// it was marked as complete.
type Task struct {
text string
creationDate time.Time
finishedDate time.Time
hash string
}
// Validate checks if a task contains valid data and corrects common errors.
func (t *Task) Validate() error {
t.text = strings.TrimSpace(t.text)
if strings.Contains(t.text, "\n") {
return fmt.Errorf("Task cannot contain newline")
}
return nil
}
// Serialize converts a Task object to a taskline that can be written to a
// taskfile.
func (t *Task) Serialize() (data []byte) {
escapedText := strings.TrimSpace(strings.ReplaceAll(t.text, `|`, `\|`))
creationString := t.creationDate.Format(FullDateFormat)
finishedString := t.finishedDate.Format(FullDateFormat)
line := fmt.Sprintf("%s | id:%s, creation:%s, finished:%s\n", escapedText, t.hash, creationString, finishedString)
data = []byte(line)
return
}
// ParseTask takes a line from a taskfile and creates a Task object from it.
func ParseTask(line string) (newTask Task, err error) {
line = strings.TrimSpace(line)
newTask.creationDate = time.Now()
newTask.finishedDate = time.Unix(0, 0)
if line == "" {
err = fmt.Errorf("ignore")
return
}
// If the line does not contain a part separator, interpret it as a new
// task.
if !SeparatorPattern.MatchString(line) {
newTask = NewTask(strings.ReplaceAll(line, `\|`, `|`))
err = fmt.Errorf("writeNewMeta")
return
}
// Split task text and syncdata on separator '|'
// ----------------------------
// [0] + 1 used since the pattern consumes the preceding character in order
// to check if it is escaped
separatorLocation := SeparatorPattern.FindStringIndex(line)[0] + 1
newTask.text = strings.TrimSpace(strings.ReplaceAll(line[:separatorLocation], `\|`, `|`))
metadata := strings.TrimSpace(line[separatorLocation+1:])
parsedCrDate := CreationDatePattern.FindStringSubmatch(metadata)
if len(parsedCrDate) != 0 {
creationDate, err := time.Parse(FullDateFormat, parsedCrDate[1])
if err == nil {
newTask.creationDate = creationDate
} else {
Warn("Could not parse creation date: \"%s\". Using current time.", parsedCrDate[1])
}
} else {
err = fmt.Errorf("writeNewMeta")
}
parsedFiDate := FinishedDatePattern.FindStringSubmatch(metadata)
if len(parsedFiDate) != 0 {
finishedDate, err := time.Parse(FullDateFormat, parsedFiDate[1])
if err == nil {
newTask.finishedDate = finishedDate
} else {
Warn("Could not parse finished date: \"%s\". Using Unix epoch.")
}
} else {
err = fmt.Errorf("writeNewMeta")
}
parsedHash := HashPattern.FindStringSubmatch(metadata)
if len(parsedHash) != 0 {
newTask.hash = parsedHash[1]
} else {
newTask.hash = hexHash(newTask.text)
err = fmt.Errorf("writeNewMeta")
}
return
}
// NewTask creates a task and sets default values.
func NewTask(text string) (newTask Task) {
newTask.text = text
newTask.creationDate = time.Now()
newTask.finishedDate = time.Unix(0, 0)
newTask.hash = hexHash(text)
return
}
func hexHash(s string) (hash string) {
return fmt.Sprintf("%x", sha1.Sum([]byte(s)))
}