package pttodo import ( "bytes" "os" yaml "gopkg.in/yaml.v2" ) type Root struct { Todo []Todo Scheduled []Todo Done []Todo } func NewRootFromFiles(p ...string) (Root, error) { var result Root for _, p := range p { subroot, err := NewRootFromFile(p) if err != nil { return Root{}, err } result.MergeIn(subroot) } result.AutoMove() return result, nil } func NewRootFromFile(p string) (Root, error) { if b, err := os.ReadFile(p); err == nil && len(bytes.TrimSpace(b)) == 0 { return Root{}, nil } f, err := os.Open(p) if os.IsNotExist(err) { return Root{}, nil } if err != nil { return Root{}, err } defer f.Close() var result Root if err := yaml.NewDecoder(f).Decode(&result); err != nil { todos, err2 := NewTodosFromFile(p) if err2 != nil { return Root{}, err } result.Todo = todos } result.AutoMove() return result, nil } func (root Root) Equals(other Root) bool { for i, slice := range [][2][]Todo{ [2][]Todo{root.Todo, other.Todo}, [2][]Todo{root.Scheduled, other.Scheduled}, [2][]Todo{root.Done, other.Done}, } { _ = i if !equalTodoSlices(slice[0], slice[1]) { return false } } return true } func (root *Root) AutoMove() { root.MoveScheduledToTodo() root.MoveTodoToScheduled() } func (root *Root) MoveTodoToScheduled() { for i := len(root.Todo) - 1; i >= 0; i-- { if !root.Todo[i].Schedule.isFixedFuture() { continue } root.Scheduled = append(root.Scheduled, root.Todo[i]) for j := i; j < len(root.Todo)-1; j++ { root.Todo[j] = root.Todo[j+1] } root.Todo = root.Todo[:len(root.Todo)-1] } } func (root *Root) MoveScheduledToTodo() { for i := len(root.Scheduled) - 1; i >= 0; i-- { if root.Scheduled[i].Triggered() { root.Todo = append(root.Todo, root.Scheduled[i]) root.Scheduled[i] = root.Scheduled[len(root.Scheduled)-1] root.Scheduled = root.Scheduled[:len(root.Scheduled)-1] if i < len(root.Scheduled)-1 { i += 1 } } } } func (root *Root) MergeIn(root2 Root) { for _, listPair := range [][2]*[]Todo{ [2]*[]Todo{&root.Todo, &root2.Todo}, [2]*[]Todo{&root.Scheduled, &root2.Scheduled}, [2]*[]Todo{&root.Done, &root2.Done}, } { for _, candidate := range *listPair[1] { found := false for i := range *listPair[0] { found = found || ((*listPair[0])[i].Todo == candidate.Todo) } if !found { *listPair[0] = append(*listPair[0], candidate) } } } } func (root Root) MarshalYAML() (interface{}, error) { for i := range root.Todo { root.Todo[i].writeTS = false } for i := range root.Scheduled { root.Scheduled[i].writeTS = true } for i := range root.Done { root.Done[i].writeTS = true } type Alt Root return (Alt)(root), nil }