package main // Workstack or ws for short is a program that manages To-Do's in a stack-based fashion. It tries // to guide your focus to your three most important tasks such that you do not get distracted by // other tasks. // Every task added starts as inactive "[ ]" and can be marked as done by changing the status to "[x]". // When the programs exits Tasks are saved to a tasks.gob file, this will truncate (os.Create) the // existing file. // TODO's // - tags // - command to add/delete tags so that we can programatically add them // - parameter to add to specify a tag could be a format in the string or -flag // - edit: replace a Task's text // - import: read multiple lines from stdin and import them as taks import ( "bufio" "encoding/gob" "errors" "fmt" "io" "os" "strconv" "time" ) var ( // Stack of active tasks Tasks []string // Completede tasks TasksDone []TaskDone // Persistent storage for Tasks gobdata string = "tasks.gob" DateLayout string = "15:04:05 02/01/2006" ) const ( TASK_LIST_COUNT = 5 ) type TaskDone struct { Text string Date time.Time } func (t TaskDone) String() string { return fmt.Sprintf("(%s) %s", t.Date.Format(DateLayout), t.Text) } func usage() { fmt.Println(`usage: ws [add|done [N]|pc [N]|ls|list|reset] add Adds a task to the end of the stack done [N] Mark a task as done undone [N] Mask a task as undone pc [N] Procrastinate task a task to end of stack del [N] Delete an active task ls List active tasks list List all tasks reset Recreate the tasks.gob file tasks.gob: Where the tasks are saved, the default path is $HOME/sync/share/tasks.gob N: When N is not provided the default is 1`) os.Exit(1) } // Helper function for parsing an int argument to a command, takes as parameter the current argument // count and parses os.Args to return an int. // If there is no argument provided returns 0 else it returns the argument provided on the command // line. func NArg(c int) int { var n int var err error if len(os.Args) < c { // no arguments n = 0 } else { n, err = strconv.Atoi(os.Args[2]) if err != nil { panic(err) } if !(len(Tasks) > n-1 && n >= 1) { fmt.Println("N out of range.") os.Exit(3) } n -= 1 } return n } func main() { // create tasks.gob file var dec *gob.Decoder var f *os.File var err error // Open/Create gob data file if not exist { p := os.Getenv("HOME") if p == "" { panic("HOME var not set.") } gobdata = p + "/sync/share/" + gobdata f, err = os.Open(gobdata) if errors.Is(err, os.ErrNotExist) { // Do nothing } else if err != nil { panic(err) } else { dec = gob.NewDecoder(f) err = dec.Decode(&TasksDone) if err != nil { panic(err) } err = dec.Decode(&Tasks) if err != nil { panic(err) } } } if len(os.Args) == 1 { // default if len(Tasks) == 0 { fmt.Println("No tasks.") return } fmt.Println("1.", Tasks[0]) os.Exit(0) } switch os.Args[1] { case "add": // prompt fmt.Fprint(os.Stderr, ">") // read console input reader := bufio.NewReader(os.Stdin) t, err := reader.ReadString('\n') if err == io.EOF { fmt.Fprint(os.Stderr, "\n") os.Exit(0) } if err != nil { panic(err) } if len(t) == 1 { os.Exit(0) } // remove newline t = t[:len(t)-1] Tasks = append(Tasks, t) case "undone": case "del": n := NArg(3) if len(Tasks) == 1 { Tasks = []string{} return } Tasks = append(Tasks[:n], Tasks[n+1:]...) case "done": n := NArg(3) TasksDone = append(TasksDone, TaskDone{ Text: Tasks[n], Date: time.Now(), }) if len(Tasks) == 1 { Tasks = []string{} return } Tasks = append(Tasks[:n], Tasks[n+1:]...) case "ls": if len(Tasks) == 0 { fmt.Println("No tasks.") break } for i := 0; i < len(Tasks) && i < TASK_LIST_COUNT; i++ { fmt.Printf("%d. %s\n", i+1, Tasks[i]) } case "list": if len(Tasks) > 0 { fmt.Println("Active:") for i, t := range Tasks { fmt.Printf("% 2d. %s\n", i+1, t) } } if len(TasksDone) > 0 { fmt.Println("Done:") for i, t := range TasksDone { fmt.Printf("% 2d. %s\n", i+1, t) } } // fmt.Println(Tasks, TasksDone) case "reset": Tasks = []string{} TasksDone = []TaskDone{} case "pc": //procrastinate // procrastinate top task by default if len(os.Args) < 3 { Tasks = append(Tasks[1:], Tasks[0]) break } var n int = NArg(3) t := Tasks[n] Tasks = append(Tasks[:n], Tasks[n+1:]...) Tasks = append(Tasks, t) default: usage() } // Save data to gobdata f, err = os.Create(gobdata) if err != nil { panic(err) } enc := gob.NewEncoder(f) err = enc.Encode(TasksDone) if err != nil { panic(err) } err = enc.Encode(Tasks) if err != nil { panic(err) } }