hlfw.ca

todo2

Download patch

ref: 4c2cab79cdc4cdaa502aa928dda32ea5cd8db595
parent: 2bc3c60c50995de3d1f49d85a97e674b572a28dd
author: halfwit <halfwit@Halfwits-MBP.hitronhub.home>
date: Tue Jan 28 04:08:01 PST 2020

Move towards having flags for printing

--- a/command.go
+++ b/command.go
@@ -1,6 +1,7 @@
 package main
 
 import (
+	"errors"
 	"flag"
 	"fmt"
 )
@@ -10,6 +11,7 @@
 	add      = "add"
 	child    = "child"
 	create   = "create"
+	destroy  = "destroy"
 	dot      = "dot"
 	generate = "generate"
 	list     = "list"
@@ -17,11 +19,12 @@
 	parent   = "parent"
 	remove   = "rm"
 	task     = "task"
+	toggle   = "toggle"
 )
 
 type command struct {
 	args   []string
-	runner func() error
+	runner func() (string, error)
 }
 
 func newCommand(arg string) (*command, error) {
@@ -29,7 +32,7 @@
 	if arg == task {
 		c.args = flag.Args()[1:]
 		c.runner = c.task
-		
+
 		return c, nil
 	}
 
@@ -71,14 +74,14 @@
 	return nil
 }
 
-func (c *command) add() error {
+func (c *command) add() (string, error) {
 	if len(c.args) != 3 {
-		return fmt.Errorf(errNoArg, add)
+		return "", fmt.Errorf(errNoArg, add)
 	}
 
 	l, err := layoutFromTodoFile()
 	if err != nil {
-		return err
+		return "", err
 	}
 
 	switch c.args[0] {
@@ -87,106 +90,92 @@
 	case child:
 		l.addLink(c.args[1], c.args[2])
 	default:
-		return fmt.Errorf(errBadArg, c.args[0])
+		return "", fmt.Errorf(errBadArg, c.args[0])
 	}
 
-	writeTodo(l)
-
-	return nil
+	return writeTodo(l)
 }
 
-func (c *command) dot() error {
+func (c *command) dot() (string, error) {
 	l, err := layoutFromTodoFile()
 	if err != nil && c.args[0] != create {
-		return err
+		return "", err
 	}
 
-	writeDot(l)
-
-	return nil
+	return writeDot(l)
 }
 
 // generate walks the file looking for a handful of known tokens
-func (c *command) generate() error {
+func (c *command) generate() (string, error) {
 	g := &generator{}
 	if g.dotTodoExists() {
 		err := g.parseTodo()
 		if err != nil {
-			return err
+			return "", err
 		}
 	}
 
 	l := g.toLayout()
-	writeTodo(l)
 
-	return nil
+	return writeTodo(l)
 }
 
-func (c *command) list() error {
+func (c *command) list() (string, error) {
 	l, err := layoutFromTodoFile()
 	if err != nil {
-		return err
+		return "", err
 	}
 
-	writeList(l)
-
-	return nil
+	return writeList(l)
 }
 
-func (c *command) listall() error {
+func (c *command) listall() (string, error) {
 	l, err := layoutFromTodoFile()
 	if err != nil {
-		return err
+		return "", err
 	}
 
-	writeListAll(l)
-
-	return nil
+	return writeListAll(l)
 }
 
-func (c *command) rm() error {
+func (c *command) rm() (string, error) {
 	if len(c.args) != 3 {
-		return fmt.Errorf(errNoArg, remove)
+		return "", fmt.Errorf(errNoArg, remove)
 	}
 
 	l, err := layoutFromTodoFile()
 	if err != nil && c.args[0] != create {
-		return err
+		return "", err
 	}
 
-	defer writeTodo(l)
-
 	switch c.args[0] {
-	case "parent":
+	case parent:
 		l.rmLink(c.args[2], c.args[1])
-	case "child":
+	case child:
 		l.rmLink(c.args[1], c.args[1])
 	default:
-		return fmt.Errorf(errBadArg, c.args[0])
+		return "", fmt.Errorf(errBadArg, c.args[0])
 	}
 
-	return nil
+	return writeTodo(l)
 }
 
-func (c *command) run() error {
+func (c *command) run() (string, error) {
 	if c.runner != nil {
-		err := c.runner()
-		if err != nil {
-			return err
-		}
+		return c.runner()
 	}
 
-	return nil
+	return "", errors.New(errNoCmd)
 }
 
-func (c *command) task() error {
+func (c *command) task() (string, error) {
 	if len(c.args) < 2 {
-		return fmt.Errorf(errNoArg, task)
+		return "", fmt.Errorf(errNoArg, task)
 	}
 
 	l, err := layoutFromTodoFile()
 	if err != nil && c.args[0] != create {
-		return err
+		return "", err
 	}
 
 	switch c.args[0] {
@@ -193,50 +182,54 @@
 	case create:
 		err = l.create(c.args[1])
 		if err != nil {
-			return err
+			return "", err
 		}
 
 		writeTodo(l)
 
-		return nil
-	case "destroy":
+		return "", nil
+	case destroy:
 		err := l.destroy(c.args[1])
 		if err != nil {
-			return err
+			return "", err
 		}
 
 		writeTodo(l)
 
-		return nil
-	case "add":
+		return "", nil
+	case add:
 		if l.taskExists(c.args[1], c.args[2]) {
-			return fmt.Errorf(errExists, c.args[1])
+			return "", fmt.Errorf(errExists, c.args[1])
 		}
 
 		if e := l.addTask(c.args[1], c.args[2]); e != nil {
-			return e
+			return "", e
 		}
 
 		writeTodo(l)
 
-		return nil
-	case "rm":
+		return "", nil
+	case remove:
 		if !l.taskExists(c.args[1], c.args[2]) {
-			return fmt.Errorf(errNoEntry, c.args[2])
+			return "", fmt.Errorf(errNoEntry, c.args[2])
 		}
 
-		defer writeTodo(l)
+		if e := l.rmTask(c.args[1], c.args[2]); e != nil {
+			return "", e
+		}
 
-		return l.rmTask(c.args[1], c.args[2])
-	case "toggle":
+		return writeTodo(l)
+	case toggle:
 		if !l.taskExists(c.args[1], c.args[2]) {
-			return fmt.Errorf(errNoEntry, c.args[1])
+			return "", fmt.Errorf(errNoEntry, c.args[1])
 		}
 
-		defer writeTodo(l)
+		if e := l.toggleTask(c.args[1], c.args[2]); e != nil {
+			return "", e
+		}
 
-		return l.toggleTask(c.args[1], c.args[2])
+		return writeTodo(l)
 	default:
-		return fmt.Errorf(errBadArg, c.args[0])
+		return "", fmt.Errorf(errBadArg, c.args[0])
 	}
 }
--- a/dag.go
+++ b/dag.go
@@ -1,19 +1,17 @@
 package main
 
 import (
-	"log"
-
 	"github.com/goombaio/dag"
 )
 
-func dagFromLayout(l *Layout) *dag.DAG {
+func dagFromLayout(l *Layout) (*dag.DAG, error) {
 	dg := dag.NewDAG()
 	dm := make(map[string]*dag.Vertex)
-	
+
 	for _, job := range l.Jobs {
 		dm[job.Key] = dag.NewVertex(job.Key, job)
 		if e := dg.AddVertex(dm[job.Key]); e != nil {
-			log.Println(e)
+			return nil, e
 		}
 	}
 
@@ -29,11 +27,11 @@
 				}
 
 				if e := dg.AddEdge(dm[job.Key], dm[other.Key]); e != nil {
-					log.Println(e)
+					return nil, e
 				}
 			}
 		}
 	}
 
-	return dg
+	return dg, nil
 }
--- a/generate.go
+++ b/generate.go
@@ -16,7 +16,7 @@
 
 func (g *generator) parseTodo() error {
 	var err error
-	
+
 	g.existing, err = layoutFromTodoFile()
 	if err != nil {
 		return err
--- a/layout.go
+++ b/layout.go
@@ -4,6 +4,7 @@
 	"bufio"
 	"errors"
 	"fmt"
+
 	"os"
 	"regexp"
 	"strings"
@@ -70,7 +71,7 @@
 func parseTags(sc *bufio.Scanner) []string {
 	txt := sc.Text()
 	n := strings.Index(txt, ":")
-	
+
 	return stringToTags(txt[:n])
 }
 
@@ -179,7 +180,7 @@
 	r := regexp.MustCompile(`([^\[\]]*)\s?(\[.*\])+\s?(.*)`)
 
 	matches := r.FindAllStringSubmatch(incoming, -1)
-	if matches != nil && len(matches[0]) < 3 {
+	if matches == nil || len(matches[0]) < 3 {
 		return nil, errors.New("could not parse string")
 	}
 
--- a/todo.go
+++ b/todo.go
@@ -1,15 +1,24 @@
 package main
 
 import (
+	"flag"
+	"fmt"
 	"log"
-	"os"
 )
 
 func main() {
-	cmd, err := newCommand(os.Args[0])
+	flag.Parse()
+
+	cmd, err := newCommand(flag.Arg(0))
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	log.Fatal(cmd.run())
+	s, err := cmd.run()
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// TODO(halfwit) Switch to our own writer type and pass in os.Stdout
+	fmt.Printf("%s", s)
 }
--- a/write.go
+++ b/write.go
@@ -1,8 +1,8 @@
 package main
 
 import (
+	"errors"
 	"fmt"
-	"log"
 	"os"
 	"strings"
 	"text/template"
@@ -19,10 +19,10 @@
 {{end}}{{end}}`
 
 // Writer that creates our normal file
-func writeTodo(l *Layout) {
+func writeTodo(l *Layout) (string, error) {
 	wr, err := os.Create(".todo")
 	if err != nil {
-		log.Fatal(err)
+		return "", err
 	}
 
 	defer wr.Close()
@@ -29,14 +29,15 @@
 
 	t := template.Must(template.New("todoTmpl").Parse(todoTmpl))
 
-	err = t.Execute(wr, l)
-	if err != nil {
-		log.Fatal(err)
+	if e := t.Execute(wr, l); e != nil {
+		return "", e
 	}
+
+	return "", nil
 }
 
 // Writer that outputs in dot format
-func writeDot(l *Layout) {
+func writeDot(l *Layout) (string, error) {
 	var sb strings.Builder
 
 	sb.WriteString("digraph depgraph {\n\trankdir=RL;\n")
@@ -67,7 +68,7 @@
 			for _, entry := range job.Tasks[0].Entries {
 				sb.WriteString("\n")
 				sb.WriteString(entry.Desc)
-				
+
 				if entry.Done {
 					sb.WriteString(" ✓")
 				}
@@ -84,18 +85,25 @@
 		}
 
 		for _, req := range job.Requires {
-			sb.WriteString(fmt.Sprintf("%s -> %s;\n", job.Key, req))
+			sb.WriteString(fmt.Sprintf("%s -> %s;\n", req, job.Key))
 		}
 	}
 
 	sb.WriteString("}\n")
-	fmt.Println(sb.String())
+	return sb.String(), nil
 }
 
 // Writer that outputs only leaves
-func writeList(l *Layout) {
+func writeList(l *Layout) (string, error) {
+	var sb strings.Builder
+
 	l.removeCompleted()
-	d := dagFromLayout(l)
+
+	d, err := dagFromLayout(l)
+	if err != nil {
+		return "", err
+	}
+
 	leaves := d.SinkVertices()
 
 	for _, leaf := range leaves {
@@ -102,7 +110,7 @@
 		job := leaf.Value.(*Job)
 
 		for _, t := range job.Tasks {
-			fmt.Printf("%v - %s\n", job.Tags, t.Title)
+			fmt.Fprintf(&sb, "%v - %s\n", job.Tags, t.Title)
 
 			for _, e := range t.Entries {
 				var f rune
@@ -114,23 +122,31 @@
 					f = '✗'
 				}
 
-				fmt.Printf(" %c %s\n", f, e.Desc)
+				fmt.Fprintf(&sb, " %c %s\n", f, e.Desc)
 			}
 		}
 	}
+
+	return sb.String(), nil
 }
 
 // Writer that outputs all nodes
-func writeListAll(l *Layout) {
-	var walk func(v *dag.Vertex)
+func writeListAll(l *Layout) (string, error) {
+	var walk func(v *dag.Vertex) error
+	var sb strings.Builder
 
-	d := dagFromLayout(l)
+	d, err := dagFromLayout(l)
+	if err != nil {
+		return "", err
+	}
+
 	seen := map[*Job]bool{}
 
-	walk = func(v *dag.Vertex) {
+	walk = func(v *dag.Vertex) error {
+
 		job := v.Value.(*Job)
 		if seen[job] {
-			return
+			return errors.New(errBadTodo)
 		}
 
 		seen[job] = true
@@ -137,7 +153,7 @@
 
 		children, err := d.Successors(v)
 		if err != nil {
-			log.Fatal(err)
+			return err
 		}
 
 		for _, child := range children {
@@ -145,7 +161,7 @@
 		}
 
 		for _, t := range job.Tasks {
-			fmt.Printf("[%v] - %s\n", job.Key, t.Title)
+			fmt.Fprintf(&sb, "[%v] - %s\n", job.Key, t.Title)
 
 			for _, e := range t.Entries {
 				var f rune
@@ -157,12 +173,18 @@
 					f = '✗'
 				}
 
-				fmt.Printf(" %c %s\n", f, e.Desc)
+				fmt.Fprintf(&sb, " %c %s\n", f, e.Desc)
 			}
 		}
+
+		return nil
 	}
 
 	for _, t := range d.SourceVertices() {
-		walk(t)
+		if e := walk(t); e != nil {
+			return "", e
+		}
 	}
+
+	return sb.String(), nil
 }