added Complete() and Reopen() to Task.

This commit is contained in:
jamesclonk@jamesclonk.ch 2014-01-04 14:13:37 +01:00
parent caa944bb71
commit 48dacccdf1
12 changed files with 364 additions and 58 deletions

48
example_test.go Normal file
View File

@ -0,0 +1,48 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package todotxt
import (
"fmt"
"log"
)
func ExampleLoadFromFilename() {
if tasklist, err := LoadFromFilename("todo.txt"); err != nil {
log.Fatal(err)
} else {
fmt.Print(tasklist) // String representation of TaskList works as expected.
}
// Output:
// (A) Call Mom @Phone +Family
// (A) Schedule annual checkup +Health
// (B) Outline chapter 5 @Computer +Novel
// (C) Add cover sheets @Office +TPSReports
// Plan backyard herb garden @Home
// Pick up milk @GroceryStore
// Research self-publishing services @Computer +Novel
// x Download Todo.txt mobile app @Phone
}
func ExampleTaskList_LoadFromFilename() {
var tasklist TaskList
// This will overwrite whatever was in the tasklist before.
// Irrelevant here since the list is still empty.
if err := tasklist.LoadFromFilename("todo.txt"); err != nil {
log.Fatal(err)
}
fmt.Println(tasklist[0].Todo) // Text part of first task (Call Mom)
fmt.Println(tasklist[2].Contexts) // Slice of contexts from third task ([Computer])
fmt.Println(tasklist[3].Priority) // Priority of fourth task (C)
fmt.Println(tasklist[7].Completed) // Completed flag of eigth task (true)
// Output:
// Call Mom
// [Computer]
// C
// true
}

32
sort.go
View File

@ -35,7 +35,7 @@ func (tasklist *TaskList) Sort(sortFlag int) error {
case SORT_DUE_DATE_ASC, SORT_DUE_DATE_DESC: case SORT_DUE_DATE_ASC, SORT_DUE_DATE_DESC:
tasklist.sortByDueDate(sortFlag) tasklist.sortByDueDate(sortFlag)
default: default:
return errors.New("Unrecognized sort option") return errors.New("unrecognized sort option")
} }
return nil return nil
} }
@ -68,19 +68,17 @@ func (tasklist *TaskList) sortBy(by func(t1, t2 *Task) bool) *TaskList {
func (tasklist *TaskList) sortByPriority(order int) *TaskList { func (tasklist *TaskList) sortByPriority(order int) *TaskList {
tasklist.sortBy(func(t1, t2 *Task) bool { tasklist.sortBy(func(t1, t2 *Task) bool {
if order == SORT_PRIORITY_DESC { // DESC if order == SORT_PRIORITY_ASC { // ASC
if t1.HasPriority() && t2.HasPriority() {
return t1.Priority > t2.Priority
} else {
return !t1.HasPriority()
}
} else { // ASC
if t1.HasPriority() && t2.HasPriority() { if t1.HasPriority() && t2.HasPriority() {
return t1.Priority < t2.Priority return t1.Priority < t2.Priority
} else {
return t1.HasPriority()
} }
return t1.HasPriority()
} }
// DESC
if t1.HasPriority() && t2.HasPriority() {
return t1.Priority > t2.Priority
}
return !t1.HasPriority()
}) })
return tasklist return tasklist
} }
@ -89,16 +87,14 @@ func sortByDate(asc bool, hasDate1, hasDate2 bool, date1, date2 time.Time) bool
if asc { // ASC if asc { // ASC
if hasDate1 && hasDate2 { if hasDate1 && hasDate2 {
return date1.Before(date2) return date1.Before(date2)
} else {
return hasDate2
}
} else { // DESC
if hasDate1 && hasDate2 {
return date1.After(date2)
} else {
return !hasDate2
} }
return hasDate2
} }
// DESC
if hasDate1 && hasDate2 {
return date1.After(date2)
}
return !hasDate2
} }
func (tasklist *TaskList) sortByCreatedDate(order int) *TaskList { func (tasklist *TaskList) sortByCreatedDate(order int) *TaskList {

View File

@ -14,7 +14,9 @@ func TestTaskSortByPriority(t *testing.T) {
testTasklist = testTasklist[taskId : taskId+6] testTasklist = testTasklist[taskId : taskId+6]
testTasklist.Sort(SORT_PRIORITY_ASC) if err := testTasklist.Sort(SORT_PRIORITY_ASC); err != nil {
t.Fatal(err)
}
testExpected = "(A) 2012-01-30 Call Mom @Call @Phone +Family" testExpected = "(A) 2012-01-30 Call Mom @Call @Phone +Family"
testGot = testTasklist[0].Task() testGot = testTasklist[0].Task()
@ -52,7 +54,9 @@ func TestTaskSortByPriority(t *testing.T) {
t.Errorf("Expected Task[6] after Sort() to be [%s], but got [%s]", testExpected, testGot) t.Errorf("Expected Task[6] after Sort() to be [%s], but got [%s]", testExpected, testGot)
} }
testTasklist.Sort(SORT_PRIORITY_DESC) if err := testTasklist.Sort(SORT_PRIORITY_DESC); err != nil {
t.Fatal(err)
}
testExpected = "x 2014-01-03 Create golang library @Go +go-todotxt due:2014-01-05" testExpected = "x 2014-01-03 Create golang library @Go +go-todotxt due:2014-01-05"
testGot = testTasklist[0].Task() testGot = testTasklist[0].Task()
@ -97,7 +101,9 @@ func TestTaskSortByCreatedDate(t *testing.T) {
testTasklist = testTasklist[taskId : taskId+5] testTasklist = testTasklist[taskId : taskId+5]
testTasklist.Sort(SORT_CREATED_DATE_ASC) if err := testTasklist.Sort(SORT_CREATED_DATE_ASC); err != nil {
t.Fatal(err)
}
testExpected = "x 2014-01-03 Create golang library @Go +go-todotxt due:2014-01-05" testExpected = "x 2014-01-03 Create golang library @Go +go-todotxt due:2014-01-05"
testGot = testTasklist[0].Task() testGot = testTasklist[0].Task()
@ -129,7 +135,9 @@ func TestTaskSortByCreatedDate(t *testing.T) {
t.Errorf("Expected Task[5] after Sort() to be [%s], but got [%s]", testExpected, testGot) t.Errorf("Expected Task[5] after Sort() to be [%s], but got [%s]", testExpected, testGot)
} }
testTasklist.Sort(SORT_CREATED_DATE_DESC) if err := testTasklist.Sort(SORT_CREATED_DATE_DESC); err != nil {
t.Fatal(err)
}
testExpected = "x (C) 2014-01-01 Create golang library documentation @Go +go-todotxt due:2014-01-12" testExpected = "x (C) 2014-01-01 Create golang library documentation @Go +go-todotxt due:2014-01-12"
testGot = testTasklist[0].Task() testGot = testTasklist[0].Task()
@ -168,7 +176,9 @@ func TestTaskSortByCompletedDate(t *testing.T) {
testTasklist = testTasklist[taskId : taskId+6] testTasklist = testTasklist[taskId : taskId+6]
testTasklist.Sort(SORT_COMPLETED_DATE_ASC) if err := testTasklist.Sort(SORT_COMPLETED_DATE_ASC); err != nil {
t.Fatal(err)
}
testExpected = "x Download Todo.txt mobile app @Phone" testExpected = "x Download Todo.txt mobile app @Phone"
testGot = testTasklist[0].Task() testGot = testTasklist[0].Task()
@ -206,7 +216,9 @@ func TestTaskSortByCompletedDate(t *testing.T) {
t.Errorf("Expected Task[6] after Sort() to be [%s], but got [%s]", testExpected, testGot) t.Errorf("Expected Task[6] after Sort() to be [%s], but got [%s]", testExpected, testGot)
} }
testTasklist.Sort(SORT_COMPLETED_DATE_DESC) if err := testTasklist.Sort(SORT_COMPLETED_DATE_DESC); err != nil {
t.Fatal(err)
}
testExpected = "x 2014-01-04 2014-01-01 Create some more golang library test cases @Go +go-todotxt" testExpected = "x 2014-01-04 2014-01-01 Create some more golang library test cases @Go +go-todotxt"
testGot = testTasklist[0].Task() testGot = testTasklist[0].Task()
@ -251,7 +263,9 @@ func TestTaskSortByDueDate(t *testing.T) {
testTasklist = testTasklist[taskId : taskId+4] testTasklist = testTasklist[taskId : taskId+4]
testTasklist.Sort(SORT_DUE_DATE_ASC) if err := testTasklist.Sort(SORT_DUE_DATE_ASC); err != nil {
t.Fatal(err)
}
testExpected = "x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt" testExpected = "x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt"
testGot = testTasklist[0].Task() testGot = testTasklist[0].Task()
@ -277,7 +291,9 @@ func TestTaskSortByDueDate(t *testing.T) {
t.Errorf("Expected Task[4] after Sort() to be [%s], but got [%s]", testExpected, testGot) t.Errorf("Expected Task[4] after Sort() to be [%s], but got [%s]", testExpected, testGot)
} }
testTasklist.Sort(SORT_DUE_DATE_DESC) if err := testTasklist.Sort(SORT_DUE_DATE_DESC); err != nil {
t.Fatal(err)
}
testExpected = "(B) 2013-12-01 Outline chapter 5 @Computer +Novel Level:5 private:false due:2014-02-17" testExpected = "(B) 2013-12-01 Outline chapter 5 @Computer +Novel Level:5 private:false due:2014-02-17"
testGot = testTasklist[0].Task() testGot = testTasklist[0].Task()
@ -303,3 +319,13 @@ func TestTaskSortByDueDate(t *testing.T) {
t.Errorf("Expected Task[4] after Sort() to be [%s], but got [%s]", testExpected, testGot) t.Errorf("Expected Task[4] after Sort() to be [%s], but got [%s]", testExpected, testGot)
} }
} }
func TestTaskSortError(t *testing.T) {
testTasklist.LoadFromFilename(testInputSort)
if err := testTasklist.Sort(123); err == nil {
t.Errorf("Expected Sort() to fail because of unrecognized sort option, but it didn't!")
} else if err.Error() != "unrecognized sort option" {
t.Error(err)
}
}

28
task.go
View File

@ -67,7 +67,7 @@ func (task Task) String() string {
if len(task.AdditionalTags) > 0 { if len(task.AdditionalTags) > 0 {
// Sort map alphabetically by keys // Sort map alphabetically by keys
keys := make([]string, 0, len(task.AdditionalTags)) keys := make([]string, 0, len(task.AdditionalTags))
for key, _ := range task.AdditionalTags { for key := range task.AdditionalTags {
keys = append(keys, key) keys = append(keys, key)
} }
sort.Strings(keys) sort.Strings(keys)
@ -106,7 +106,25 @@ func (task *Task) HasDueDate() bool {
// HasCompletedDate returns true if the task has a completed date. // HasCompletedDate returns true if the task has a completed date.
func (task *Task) HasCompletedDate() bool { func (task *Task) HasCompletedDate() bool {
return !task.CompletedDate.IsZero() return !task.CompletedDate.IsZero() && task.Completed
}
// Complete sets Task.Completed to 'true' if the task was not already completed.
// Also sets Task.CompletedDate to time.Now()
func (task *Task) Complete() {
if !task.Completed {
task.Completed = true
task.CompletedDate = time.Now()
}
}
// Reopen sets Task.Completed to 'false' if the task was completed.
// Also resets Task.CompletedDate.
func (task *Task) Reopen() {
if task.Completed {
task.Completed = false
task.CompletedDate = time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC) // time.IsZero() value
}
} }
// IsOverdue returns true if due date is in the past. // IsOverdue returns true if due date is in the past.
@ -116,9 +134,8 @@ func (task *Task) HasCompletedDate() bool {
func (task *Task) IsOverdue() bool { func (task *Task) IsOverdue() bool {
if task.HasDueDate() { if task.HasDueDate() {
return task.DueDate.Before(time.Now()) return task.DueDate.Before(time.Now())
} else {
return false
} }
return false
} }
// Due returns the duration passed since due date, or until due date from now. // Due returns the duration passed since due date, or until due date from now.
@ -129,7 +146,6 @@ func (task *Task) IsOverdue() bool {
func (task *Task) Due() time.Duration { func (task *Task) Due() time.Duration {
if task.IsOverdue() { if task.IsOverdue() {
return time.Now().Sub(task.DueDate) return time.Now().Sub(task.DueDate)
} else {
return task.DueDate.Sub(time.Now())
} }
return task.DueDate.Sub(time.Now())
} }

View File

@ -263,21 +263,21 @@ func TestTaskCompleted(t *testing.T) {
testExpected = true testExpected = true
testGot = testTasklist[taskId-1].Completed testGot = testTasklist[taskId-1].Completed
if testGot != testExpected { if testGot != testExpected {
t.Errorf("Expected Task[%d] not to be completed, but got '%v'", taskId, testGot) t.Errorf("Expected Task[%d] to be completed, but got '%v'", taskId, testGot)
} }
taskId++ taskId++
testExpected = true testExpected = true
testGot = testTasklist[taskId-1].Completed testGot = testTasklist[taskId-1].Completed
if testGot != testExpected { if testGot != testExpected {
t.Errorf("Expected Task[%d] not to be completed, but got '%v'", taskId, testGot) t.Errorf("Expected Task[%d] to be completed, but got '%v'", taskId, testGot)
} }
taskId++ taskId++
testExpected = true testExpected = true
testGot = testTasklist[taskId-1].Completed testGot = testTasklist[taskId-1].Completed
if testGot != testExpected { if testGot != testExpected {
t.Errorf("Expected Task[%d] not to be completed, but got '%v'", taskId, testGot) t.Errorf("Expected Task[%d] to be completed, but got '%v'", taskId, testGot)
} }
taskId++ taskId++
@ -379,6 +379,145 @@ func TestTaskIsOverdue(t *testing.T) {
t.Errorf("Expected Task[%d] to be due since 72 hours, but got '%v'", taskId, testGot) t.Errorf("Expected Task[%d] to be due since 72 hours, but got '%v'", taskId, testGot)
} }
taskId++ taskId++
testGot = testTasklist[taskId-1].IsOverdue()
if testGot.(bool) {
t.Errorf("Expected Task[%d] not to be overdue, but got '%v'", taskId, testGot)
}
taskId++
}
func TestTaskComplete(t *testing.T) {
testTasklist.LoadFromFilename(testInputTask)
taskId := 44
// first 4 tasks should all match the same tests
for i := 0; i < 4; i++ {
testExpected = false
testGot = testTasklist[taskId-1].Completed
if testGot != testExpected {
t.Errorf("Expected Task[%d] not to be completed, but got '%v'", taskId, testGot)
}
testGot = testTasklist[taskId-1].HasCompletedDate()
if testGot != testExpected {
t.Errorf("Expected Task[%d] not to have a completed date, but got '%v'", taskId, testGot)
}
testTasklist[taskId-1].Complete()
testExpected = true
testGot = testTasklist[taskId-1].Completed
if testGot != testExpected {
t.Errorf("Expected Task[%d] to be completed, but got '%v'", taskId, testGot)
}
testGot = testTasklist[taskId-1].HasCompletedDate()
if testGot != testExpected {
t.Errorf("Expected Task[%d] to have a completed date, but got '%v'", taskId, testGot)
}
testExpected = time.Now().Format(DateLayout)
testGot = testTasklist[taskId-1].CompletedDate.Format(DateLayout)
if testGot != testExpected {
t.Errorf("Expected Task[%d] to have a completed date of '%v', but got '%v'", taskId, testExpected, testGot)
}
taskId++
}
testExpected = true
testGot = testTasklist[taskId-1].Completed
if testGot != testExpected {
t.Errorf("Expected Task[%d] to be completed, but got '%v'", taskId, testGot)
}
testGot = testTasklist[taskId-1].HasCompletedDate()
if testGot != testExpected {
t.Errorf("Expected Task[%d] to have a completed date, but got '%v'", taskId, testGot)
}
testTasklist[taskId-1].Complete()
testGot = testTasklist[taskId-1].Completed // should be unchanged
if testGot != testExpected {
t.Errorf("Expected Task[%d] to be completed, but got '%v'", taskId, testGot)
}
testGot = testTasklist[taskId-1].HasCompletedDate() // should be unchanged
if testGot != testExpected {
t.Errorf("Expected Task[%d] to have a completed date, but got '%v'", taskId, testGot)
}
testExpected = "2012-01-01" // should be unchanged
testGot = testTasklist[taskId-1].CompletedDate.Format(DateLayout)
if testGot != testExpected {
t.Errorf("Expected Task[%d] to have a completed date of '%v', but got '%v'", taskId, testExpected, testGot)
}
taskId++
}
func TestTaskReopen(t *testing.T) {
testTasklist.LoadFromFilename(testInputTask)
taskId := 49
// the first 2 tasks should match the same tests
for i := 0; i < 2; i++ {
testExpected = true
testGot = testTasklist[taskId-1].Completed
if testGot != testExpected {
t.Errorf("Expected Task[%d] to be completed, but got '%v'", taskId, testGot)
}
testExpected = false
testGot = testTasklist[taskId-1].HasCompletedDate()
if testGot != testExpected {
t.Errorf("Expected Task[%d] to have a completed date, but got '%v'", taskId, testGot)
}
testTasklist[taskId-1].Reopen()
testExpected = false
testGot = testTasklist[taskId-1].Completed
if testGot != testExpected {
t.Errorf("Expected Task[%d] to not be completed, but got '%v'", taskId, testGot)
}
testGot = testTasklist[taskId-1].HasCompletedDate()
if testGot != testExpected {
t.Errorf("Expected Task[%d] to not have a completed date, but got '%v'", taskId, testGot)
}
taskId++
}
// the next 3 tasks should all match the same tests
for i := 0; i < 3; i++ {
testExpected = true
testGot = testTasklist[taskId-1].Completed
if testGot != testExpected {
t.Errorf("Expected Task[%d] to be completed, but got '%v'", taskId, testGot)
}
testGot = testTasklist[taskId-1].HasCompletedDate()
if testGot != testExpected {
t.Errorf("Expected Task[%d] to have a completed date, but got '%v'", taskId, testGot)
}
testTasklist[taskId-1].Reopen()
testExpected = false
testGot = testTasklist[taskId-1].Completed
if testGot != testExpected {
t.Errorf("Expected Task[%d] to not be completed, but got '%v'", taskId, testGot)
}
testGot = testTasklist[taskId-1].HasCompletedDate()
if testGot != testExpected {
t.Errorf("Expected Task[%d] to not have a completed date, but got '%v'", taskId, testGot)
}
taskId++
}
testExpected = false
testGot = testTasklist[taskId-1].Completed
if testGot != testExpected {
t.Errorf("Expected Task[%d] to be completed, but got '%v'", taskId, testGot)
}
testGot = testTasklist[taskId-1].HasCompletedDate()
if testGot != testExpected {
t.Errorf("Expected Task[%d] to have a completed date, but got '%v'", taskId, testGot)
}
testTasklist[taskId-1].Reopen()
testGot = testTasklist[taskId-1].Completed // should be unchanged
if testGot != testExpected {
t.Errorf("Expected Task[%d] to be completed, but got '%v'", taskId, testGot)
}
testGot = testTasklist[taskId-1].HasCompletedDate() // should be unchanged
if testGot != testExpected {
t.Errorf("Expected Task[%d] to have a completed date, but got '%v'", taskId, testGot)
}
taskId++
} }
func compareSlices(list1 []string, list2 []string) bool { func compareSlices(list1 []string, list2 []string) bool {

View File

@ -59,3 +59,20 @@ x 2014-01-03 2014-01-01 Create some more golang library test cases @Go +go-todot
x 2014-01-04 (B) 2013-12-30 Create golang library @Go +go-todotxt due:2014-01-02 x 2014-01-04 (B) 2013-12-30 Create golang library @Go +go-todotxt due:2014-01-02
(B) 2013-12-01 private:false Outline chapter 5 +Novel @Computer due:2017-07-17 Level:5 (B) 2013-12-01 private:false Outline chapter 5 +Novel @Computer due:2017-07-17 Level:5
Research self-publishing services +Novel +Novel +Novel due:2014-01-01 Research self-publishing services +Novel +Novel +Novel due:2014-01-01
x 2014-01-03 2014-01-01 Create some more golang library test cases @Go +go-todotxt
# Complete test cases
Create golang library @Go +go-todotxt due:2014-01-05
(A) @Phone Call Mom @Call +Family
2014-01-03 Create golang library @Go +go-todotxt due:2014-01-05
(B) 2013-12-30 Create golang library test cases @Go +go-todotxt
x 2012-01-01 More Go!
# Reopen test cases
x Download Todo.txt mobile app @Phone
x (C) 2014-01-01 Create golang library documentation @Go +go-todotxt due:2014-01-12
x 2014-01-03 Create golang library @Go +go-todotxt due:2014-01-05
x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt
x 2014-01-03 2014-01-01 Create some more golang library test cases @Go +go-todotxt
(B) 2013-12-30 Create golang library test cases @Go +go-todotxt

View File

@ -0,0 +1,6 @@
x (C) 2014-01-01 Create golang library documentation @Go +go-todotxt due:2014-01-12
x Download Todo.txt mobile app @Phone
x 2014-01-03 Create golang library @Go +go-todotxt due:2014-01-05
x (C) 2014-01-01 Create golang library documentation @Go +go-todotxt due:2014-01-12
x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt
x 2014-25-04 2014-01-01 Create some more golang library test cases @Go +go-todotxt

View File

@ -0,0 +1,5 @@
2013-02-22 Pick up milk @GroceryStore
x Download Todo.txt mobile app @Phone
(B) 2013-13-01 private:false Outline chapter 5 +Novel @Computer Level:5 due:2014-02-17
x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt
x 2014-01-03 2014-01-01 Create some more golang library test cases @Go +go-todotxt

5
testdata/tasklist_dueDate_error.txt vendored Normal file
View File

@ -0,0 +1,5 @@
B) 2013-12-01 private:false Outline chapter 5 +Novel @Computer Level:5 due:2014-02-17
x 2014-01-02 (B) 2013-12-30 Create golang library test cases @Go +go-todotxt
x 2014-01-03 2014-01-01 Create some more golang library test cases @Go +go-todotxt
(B) 2013-12-01 private:false Outline chapter 5 +Novel @Computer Level:5 due:2014-02-32
x (C) 2014-01-01 Create golang library documentation @Go +go-todotxt due:2014-01-12

2
testdata/tasklist_scanner_error.txt vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -23,12 +23,13 @@ import (
// It is usually loaded from a whole todo.txt file. // It is usually loaded from a whole todo.txt file.
type TaskList []Task type TaskList []Task
// IgnoreComments can be set to 'false', in order to revert to more standard todo.txt behaviour. // IgnoreComments can be set to 'false', in order to revert to a more standard todo.txt behaviour.
// The todo.txt format does not define comments. // The todo.txt format does not define comments.
var ( var (
// Used for formatting time.Time into todo.txt date format and vice-versa. // DateLayout is used for formatting time.Time into todo.txt date format and vice-versa.
DateLayout = "2006-01-02" DateLayout = "2006-01-02"
// Ignores comments (Lines/Text starting with "#"). // IgnoreComments is used to switch ignoring of comments (lines starting with "#").
// If this is set to 'false', then lines starting with "#" will be parsed as tasks.
IgnoreComments = true IgnoreComments = true
// unexported vars // unexported vars
@ -74,10 +75,10 @@ func (tasklist *TaskList) LoadFromFile(file *os.File) error {
task.Completed = true task.Completed = true
// Check for completed date // Check for completed date
if completedDateRx.MatchString(task.Original) { if completedDateRx.MatchString(task.Original) {
if date, err := time.Parse(DateLayout, completedDateRx.FindStringSubmatch(task.Original)[1]); err != nil { if date, err := time.Parse(DateLayout, completedDateRx.FindStringSubmatch(task.Original)[1]); err == nil {
return err
} else {
task.CompletedDate = date task.CompletedDate = date
} else {
return err
} }
} }
@ -94,11 +95,11 @@ func (tasklist *TaskList) LoadFromFile(file *os.File) error {
// Check for created date // Check for created date
if createdDateRx.MatchString(task.Original) { if createdDateRx.MatchString(task.Original) {
if date, err := time.Parse(DateLayout, createdDateRx.FindStringSubmatch(task.Original)[2]); err != nil { if date, err := time.Parse(DateLayout, createdDateRx.FindStringSubmatch(task.Original)[2]); err == nil {
return err
} else {
task.CreatedDate = date task.CreatedDate = date
task.Todo = createdDateRx.ReplaceAllString(task.Todo, "") // Remove from Todo text task.Todo = createdDateRx.ReplaceAllString(task.Todo, "") // Remove from Todo text
} else {
return err
} }
} }
@ -137,10 +138,10 @@ func (tasklist *TaskList) LoadFromFile(file *os.File) error {
for _, match := range matches { for _, match := range matches {
key, value := match[2], match[3] key, value := match[2], match[3]
if key == "due" { // due date is a known addon tag, it has its own struct field if key == "due" { // due date is a known addon tag, it has its own struct field
if date, err := time.Parse(DateLayout, value); err != nil { if date, err := time.Parse(DateLayout, value); err == nil {
return err
} else {
task.DueDate = date task.DueDate = date
} else {
return err
} }
} else if key != "" && value != "" { } else if key != "" && value != "" {
tags[key] = value tags[key] = value
@ -195,8 +196,10 @@ func (tasklist *TaskList) WriteToFilename(filename string) error {
// Using *os.File instead of a filename allows to also use os.Stdin. // Using *os.File instead of a filename allows to also use os.Stdin.
func LoadFromFile(file *os.File) (TaskList, error) { func LoadFromFile(file *os.File) (TaskList, error) {
tasklist := TaskList{} tasklist := TaskList{}
err := tasklist.LoadFromFile(file) if err := tasklist.LoadFromFile(file); err != nil {
return tasklist, err return nil, err
}
return tasklist, nil
} }
// WriteToFile writes a TaskList to *os.File. // WriteToFile writes a TaskList to *os.File.
@ -209,8 +212,10 @@ func WriteToFile(tasklist *TaskList, file *os.File) error {
// LoadFromFilename loads and returns a TaskList from a file (most likely called "todo.txt"). // LoadFromFilename loads and returns a TaskList from a file (most likely called "todo.txt").
func LoadFromFilename(filename string) (TaskList, error) { func LoadFromFilename(filename string) (TaskList, error) {
tasklist := TaskList{} tasklist := TaskList{}
err := tasklist.LoadFromFilename(filename) if err := tasklist.LoadFromFilename(filename); err != nil {
return tasklist, err return nil, err
}
return tasklist, nil
} }
// WriteToFilename writes a TaskList to the specified file (most likely called "todo.txt"). // WriteToFilename writes a TaskList to the specified file (most likely called "todo.txt").

View File

@ -11,12 +11,16 @@ import (
) )
var ( var (
testInputTasklist = "testdata/tasklist_todo.txt" testInputTasklist = "testdata/tasklist_todo.txt"
testOutput = "testdata/ouput_todo.txt" testInputTasklistCreatedDateError = "testdata/tasklist_createdDate_error.txt"
testExpectedOutput = "testdata/expected_todo.txt" testInputTasklistDueDateError = "testdata/tasklist_dueDate_error.txt"
testTasklist TaskList testInputTasklistCompletedDateError = "testdata/tasklist_completedDate_error.txt"
testExpected interface{} testInputTasklistScannerError = "testdata/tasklist_scanner_error.txt"
testGot interface{} testOutput = "testdata/ouput_todo.txt"
testExpectedOutput = "testdata/expected_todo.txt"
testTasklist TaskList
testExpected interface{}
testGot interface{}
) )
func TestLoadFromFile(t *testing.T) { func TestLoadFromFile(t *testing.T) {
@ -39,6 +43,10 @@ func TestLoadFromFile(t *testing.T) {
t.Errorf("Expected TaskList to be [%s], but got [%s]", testExpected, testGot) t.Errorf("Expected TaskList to be [%s], but got [%s]", testExpected, testGot)
} }
} }
if testTasklist, err := LoadFromFile(nil); testTasklist != nil || err == nil {
t.Errorf("Expected LoadFromFile to fail, but got TaskList back: [%s]", testTasklist)
}
} }
func TestLoadFromFilename(t *testing.T) { func TestLoadFromFilename(t *testing.T) {
@ -55,6 +63,10 @@ func TestLoadFromFilename(t *testing.T) {
t.Errorf("Expected TaskList to be [%s], but got [%s]", testExpected, testGot) t.Errorf("Expected TaskList to be [%s], but got [%s]", testExpected, testGot)
} }
} }
if testTasklist, err := LoadFromFilename("some_file_that_does_not_exists.txt"); testTasklist != nil || err == nil {
t.Errorf("Expected LoadFromFilename to fail, but got TaskList back: [%s]", testTasklist)
}
} }
func TestWriteFile(t *testing.T) { func TestWriteFile(t *testing.T) {
@ -192,7 +204,9 @@ func TestTaskListWriteFilename(t *testing.T) {
} }
func TestTaskListCount(t *testing.T) { func TestTaskListCount(t *testing.T) {
testTasklist.LoadFromFilename(testInputTasklist) if err := testTasklist.LoadFromFilename(testInputTasklist); err != nil {
t.Fatal(err)
}
testExpected := 63 testExpected := 63
testGot := len(testTasklist) testGot := len(testTasklist)
@ -200,3 +214,30 @@ func TestTaskListCount(t *testing.T) {
t.Errorf("Expected TaskList to contain %d tasks, but got %d", testExpected, testGot) t.Errorf("Expected TaskList to contain %d tasks, but got %d", testExpected, testGot)
} }
} }
func TestTaskListReadErrors(t *testing.T) {
if testTasklist, err := LoadFromFilename(testInputTasklistCreatedDateError); testTasklist != nil || err == nil {
t.Errorf("Expected LoadFromFilename to fail because of invalid created date, but got TaskList back: [%s]", testTasklist)
} else if err.Error() != `parsing time "2013-13-01": month out of range` {
t.Error(err)
}
if testTasklist, err := LoadFromFilename(testInputTasklistDueDateError); testTasklist != nil || err == nil {
t.Errorf("Expected LoadFromFilename to fail because of invalid due date, but got TaskList back: [%s]", testTasklist)
} else if err.Error() != `parsing time "2014-02-32": day out of range` {
t.Error(err)
}
if testTasklist, err := LoadFromFilename(testInputTasklistCompletedDateError); testTasklist != nil || err == nil {
t.Errorf("Expected LoadFromFilename to fail because of invalid completed date, but got TaskList back: [%s]", testTasklist)
} else if err.Error() != `parsing time "2014-25-04": month out of range` {
t.Error(err)
}
// really silly test
if testTasklist, err := LoadFromFilename(testInputTasklistScannerError); testTasklist != nil || err == nil {
t.Errorf("Expected LoadFromFilename to fail because of invalid file, but got TaskList back: [%s]", testTasklist)
} else if err.Error() != `bufio.Scanner: token too long` {
t.Error(err)
}
}