diff --git a/pttodo/todo.go b/pttodo/todo.go index 062b2cd..1fe9ad6 100644 --- a/pttodo/todo.go +++ b/pttodo/todo.go @@ -1,7 +1,9 @@ package pttodo import ( + "encoding/base64" "fmt" + "hash/crc32" "time" ) @@ -15,6 +17,18 @@ type Todo struct { writeTS bool } +func (todo Todo) ID() string { + hash := crc32.NewIEEE() + fmt.Fprintf(hash, "%d:%s", 0, todo.Todo) + fmt.Fprintf(hash, "%d:%s", 1, todo.Details) + fmt.Fprintf(hash, "%d:%s", 2, todo.Schedule) + fmt.Fprintf(hash, "%d:%s", 3, todo.Tags) + for i := range todo.Subtasks { + fmt.Fprintf(hash, "%d:%s", 4, todo.Subtasks[i].ID()) + } + return base64.StdEncoding.EncodeToString(hash.Sum(nil)) +} + func (todo Todo) Triggered() bool { last := todo.TS next, err := todo.Schedule.Next(last.time()) diff --git a/pttodo/todo_test.go b/pttodo/todo_test.go index 3645f18..216b8d4 100644 --- a/pttodo/todo_test.go +++ b/pttodo/todo_test.go @@ -125,3 +125,33 @@ func TestMarshalTodo(t *testing.T) { } }) } + +func TestTodoID(t *testing.T) { + cases := map[string]Todo{ + "empty": Todo{}, + "todo": Todo{Todo: "abc"}, + "details": Todo{Details: "abc"}, + "todo,details": Todo{Todo: "abc", Details: "abc"}, + } + + got := map[string]bool{} + for name, todod := range cases { + todo := todod + t.Run(name, func(t *testing.T) { + if _, ok := got[todo.ID()]; ok { + t.Error("dupe", todo.ID()) + } + got[todo.ID()] = true + t.Logf("%s: %+v", todo.ID(), todo) + todo2 := todo + todo2.TS = 1 + if todo.ID() != todo2.ID() { + t.Error("ts changed") + } + todo2.writeTS = true + if todo.ID() != todo2.ID() { + t.Error("writets changed") + } + }) + } +}