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)