hlfw.ca

todo2

Download patch

ref: d5528764f8607a6e828ac7ef8d7ac709fd076c60
parent: b598415a8e4d0921edd95d5c93f18bec803ee74c
author: Michael Misch <michaelmisch1985@gmail.com>
date: Wed Nov 27 16:14:21 PST 2019

Finish task subcommand

--- a/command.go
+++ b/command.go
@@ -4,6 +4,7 @@
 	"errors"
 	"flag"
 	"fmt"
+	"log"
 	"os"
 	"path"
 
@@ -10,9 +11,6 @@
 	"github.com/altid/fslib"
 )
 
-// Initialize a command here to run in main
-// Generally we could just run a command based on a case match
-// but should this ever need to scale, this design will help us
 type command struct {
 	mkfile string
 	args   []string
@@ -21,7 +19,7 @@
 
 func newCommand(arg string) (*command, error) {
 	c := &command{}
-	
+
 	if arg == "task" {
 		c.args = flag.Args()[1:]
 		c.runner = task
@@ -36,9 +34,21 @@
 	return c, nil
 }
 
-// This is pure bloat at the moment, a hashmap would be fine for mapping args to funcs
-// as would calling a func itself in the case match. We want to future proof against
-// needing to do any additional bookkeeping in the functions themselves
+func (c command) setEnv() error {
+
+	dir, err := os.Getwd()
+	if err != nil {
+		return err
+	}
+	data, err := fslib.UserShareDir()
+	if err != nil {
+		return err
+	}
+	os.MkdirAll(path.Join(data, "todo"), 0755)
+	c.mkfile = path.Join(data, "todo", path.Base(dir))
+	return nil
+}
+
 func (c command) setTask(arg string) error {
 	switch arg {
 	//case "help":
@@ -71,23 +81,11 @@
 	return nil
 }
 
-func (c command) setEnv() error {
-
-	dir, err := os.Getwd()
-	if err != nil {
-		return err
-	}
-	data, err := fslib.UserShareDir()
-	if err != nil {
-		return err
-	}
-	c.mkfile = path.Join(data, "todo", path.Base(dir))
-	return nil
-}
-
-// As above, broken out for posterity
 func (c *command) run() {
 	if c.runner != nil {
-		c.runner(c)
+		err := c.runner(c)
+		if err != nil {
+			log.Fatal(err)
+		}
 	}
 }
--- a/task.go
+++ b/task.go
@@ -1,6 +1,8 @@
 package main
 
 import (
+	"bufio"
+	"errors"
 	"fmt"
 	"log"
 	"os"
@@ -7,59 +9,114 @@
 	"text/template"
 )
 
-const tmplt = `{{range .tasklist .}}
-{{if and .list .title}}
-{{.title}}
-{{range .list .}}{{if .done}}[*]{{else}}[ ]{{end}} {{.desc}}{{end}}
-
+const tmplt = `{{range .TaskList}}{{.Title}}
+{{range .List}}{{if .Done}}[x] {{.Desc}}{{else}}[ ] {{.Desc}}{{end}}
 {{end}}
-{{end}}
-`
+{{end}}`
 
-/* Example:
-My entry title
-[ ] an entry
-[*] a done entry
-
-My other entry
-[ ] an entry
-[*] a done entry
-*/
-
 type layout struct {
-	tasklist []entries
+	TaskList []*entries
+	file     string
 }
 
+// Entries - set of todo items for a give task
 type entries struct {
-	title string
-	list  []entry
+	Title string
+	List  []*entry
 }
 
+// Entry - individual item of a set of tasks
 type entry struct {
-	desc string
-	done bool
+	Desc string
+	Done bool
 }
 
+func parse(file string) (*layout, error) {
+	l := &layout{
+		TaskList: []*entries{},
+		file:     file,
+	}
+	fl, err := os.Open(file)
+	if err != nil {
+		return nil, err
+	}
+	sc := bufio.NewScanner(fl)
+	for sc.Scan() {
+		if sc.Text() == "" {
+			continue
+		}
+		tl := &entries{
+			Title: sc.Text(),
+			List:  parseEntries(sc),
+		}
+		l.TaskList = append(l.TaskList, tl)
+	}
+	if err := sc.Err(); err != nil {
+		return nil, err
+	}
+
+	return l, nil
+}
+
+func parseEntries(sc *bufio.Scanner) []*entry {
+	sc.Scan()
+	en := []*entry{}
+	for {
+		d := sc.Text()
+		if len(d) < 4 {
+			return en
+		}
+		if err := sc.Err(); err != nil {
+			log.Fatal(err)
+		}
+		switch d[:3] {
+		case "[ ]":
+			en = append(en, &entry{
+				Desc: d[4:],
+				Done: false,
+			})
+		case "[x]":
+			en = append(en, &entry{
+				Desc: d[4:],
+				Done: true,
+			})
+		default:
+			return en
+		}
+		sc.Scan()
+	}
+}
+
 func task(c *command) error {
-	l, err := parse("test")
+	if len(c.args) < 2 {
+		return errors.New("Too few arguments supplied")
+	}
+	//fp, err := findEntry(c.args[1])
+	//if err != nil {
+	//return err
+	//}
+	fp := "test"
+	l, err := parse(fp)
 	if err != nil {
 		return err
 	}
+
 	switch c.args[0] {
 	case "add":
-		if len(c.args) < 3 || l.exists(c.args[1], c.args[2]) {
+		if l.exists(c.args[1], c.args[2]) {
 			return fmt.Errorf("Entry exists")
 		}
-		defer l.write()
-		return l.add(c.args[1], c.args[2])
+		l.add(c.args[1], c.args[2])
+		l.write()
+		return nil
 	case "rm":
-		if len(c.args) < 3 || !l.exists(c.args[1], c.args[2]) {
+		if !l.exists(c.args[1], c.args[2]) {
 			return fmt.Errorf("No entry found")
 		}
 		defer l.write()
 		return l.rm(c.args[1], c.args[2])
 	case "toggle":
-		if len(c.args) < 3 || !l.exists(c.args[1], c.args[2]) {
+		if !l.exists(c.args[1], c.args[2]) {
 			return fmt.Errorf("No such task/entry")
 		}
 		defer l.write()
@@ -70,22 +127,25 @@
 }
 
 func (l *layout) write() {
-	wr, err := os.Create("tmp")
+	wr, err := os.Create(l.file)
 	defer wr.Close()
 	if err != nil {
 		log.Fatal(err)
 	}
-	t := template.Must(template.New(".todo").Parse(tmplt))
-	t.Execute(wr, l)
+	t := template.Must(template.New("tmplt").Parse(tmplt))
+	err = t.Execute(wr, l)
+	if err != nil {
+		log.Fatal(err)
+	}
 }
 
-func (l *layout) exists(title, item string) bool {
-	for _, e := range l.tasklist {
-		if e.title != title {
+func (l *layout) exists(Title, item string) bool {
+	for _, e := range l.TaskList {
+		if e.Title != Title {
 			continue
 		}
-		for _, t := range e.list {
-			if t.desc == item {
+		for _, t := range e.List {
+			if t.Desc == item {
 				return true
 			}
 		}
@@ -93,56 +153,58 @@
 	return false
 }
 
-func (l *layout) add(title, item string) error {
-	for _, e := range l.tasklist {
-		if e.title != title {
+func (l *layout) add(Title, item string) error {
+	for _, e := range l.TaskList {
+		// Found existing task, append to List
+		if e.Title != Title {
 			continue
 		}
-		e.list = append(e.list, entry{
-			desc: item,
-			done: false,
+		e.List = append(e.List, &entry{
+			Desc: item,
+			Done: false,
 		})
 		return nil
 	}
-	line := entry{
-		desc: item,
-		done: false,
+	line := &entry{
+		Desc: item,
+		Done: false,
 	}
-	l.tasklist = append(l.tasklist, entries{
-		title: title,
-		list:  []entry{line},
+	l.TaskList = append(l.TaskList, &entries{
+		Title: Title,
+		List:  []*entry{line},
 	})
 	return nil
 }
 
-func (l *layout) rm(title, item string) error {
-	for _, e := range l.tasklist {
-		if e.title != title {
+func (l *layout) rm(Title, item string) error {
+	for _, e := range l.TaskList {
+		if e.Title != Title {
 			continue
 		}
-		for i, t := range e.list {
-			if t.desc != item {
+		for i, t := range e.List {
+			if t.Desc != item {
 				continue
 			}
-			if i < len(e.list)-1 {
-				copy(e.list[i:], e.list[i+1:])
+			if i < len(e.List)-1 {
+				copy(e.List[i:], e.List[i+1:])
 			}
-			e.list = e.list[:len(e.list)-1]
+			e.List = e.List[:len(e.List)-1]
 			return nil
 		}
 	}
 	return fmt.Errorf("No such task/entry")
 }
-func (l *layout) toggle(title, item string) error {
-	for _, e := range l.tasklist {
-		if e.title != title {
+
+func (l *layout) toggle(Title, item string) error {
+	for _, e := range l.TaskList {
+		if e.Title != Title {
 			continue
 		}
-		for _, t := range e.list {
-			if t.desc != item {
+		for _, t := range e.List {
+			if t.Desc != item {
 				continue
 			}
-			t.done = !t.done
+			t.Done = !t.Done
 			return nil
 		}
 	}
--- a/test
+++ b/test
@@ -1,3 +1,9 @@
-Here is a task
-[ ] and done
-[*] and not
+TODO #92930
+[x] Be awesome
+[ ] Make a cool demo
+[x] Impress everyone
+
+TODO #91210
+[ ] Watch Baywatch
+[ ] Learn To Swim
+
--- a/todo.go
+++ b/todo.go
@@ -7,7 +7,7 @@
 )
 
 var (
-	mkfile = flag.String("mkfile", "", "Alternate Makefile to use")
+	mkfile = flag.String("m", "", "Alternate Makefile to use")
 )
 
 func main() {
@@ -16,6 +16,7 @@
 		flag.Usage()
 		os.Exit(0)
 	}
+	// Build and run our command
 	cmd, err := newCommand(flag.Arg(0))
 	if err != nil {
 		log.Fatal(err)