diff options
author | Raymaekers Luca <raymaekers.luca@gmail.com> | 2024-11-10 14:06:18 +0100 |
---|---|---|
committer | Raymaekers Luca <raymaekers.luca@gmail.com> | 2024-11-10 14:07:21 +0100 |
commit | ad898317914439c33e5670d9018646799f2d6c3f (patch) | |
tree | 70047a56ae893441f3d1499d653475bb758d0ed7 | |
parent | 45e65c66bbcded97135503068881214ee644d2f7 (diff) |
Better flags parsing and "top" comamnd
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | workstack.go | 12 | ||||
-rw-r--r-- | ws/main.go | 437 |
3 files changed, 287 insertions, 169 deletions
@@ -1,14 +1,10 @@ # To Do -- [ ] Put a task at the top - [ ] ZSH Completion -- [ ] Better help for commands & options - [ ] Add descriptions to task - [ ] Edit task with editor - [ ] Create a script to add from dump file into tasks -- [ ] After pc/del do "ls" command - [ ] Add "start" command - [ ] Add tore-like reminder for shellrc -- [ ] Redo project folder structure. - [ ] Rename a task - [ ] import: read multiple lines from stdin and import them as taks - [ ] parsing text as Tasks, maybe helper program? @@ -31,5 +27,8 @@ - [ ] bug: undo a task with a tag that no longer exists # Done +- [x] After pc/del do "ls" command +- [x] Better help for commands & options +- [x] Put a task at the top - [x] implement "undone" command - [x] Add tags support diff --git a/workstack.go b/workstack.go index f7bc41c..45b5d67 100644 --- a/workstack.go +++ b/workstack.go @@ -16,10 +16,6 @@ type TaskDone struct { Date time.Time } -func (t TaskDone) String() string { - return fmt.Sprintf("(%s) %s", t.Date.Format(DateLayout), t.Task) -} - type Tag string type Task struct { @@ -27,6 +23,10 @@ type Task struct { Tag Tag } +func (t TaskDone) String() string { + return fmt.Sprintf("(%s) %s", t.Date.Format(DateLayout), t.Task) +} + func (t Task) String() string { if t.Tag != "" { return fmt.Sprintf("{%s} %s", t.Tag, t.Text) @@ -36,14 +36,12 @@ func (t Task) String() string { } var ( - // Persistent storage for Tasks - Gobdata string = "tasks.gob" DateLayout string = "15:04:05 02/01/2006" ) const ( TASK_LIST_COUNT = 5 - GOBDATA_FILENAME = "tasks.gob" + GOBDATA_FILENAME = "workstack.gob" ) func GetGobdataPath() string { @@ -3,9 +3,9 @@ package main import ( "encoding/gob" "errors" + "flag" "fmt" "os" - "strconv" "strings" "time" @@ -22,48 +22,149 @@ var ( // Persistent storage for Tasks ) -// Search for arg in Args and return the index at which it was found -// Returns -1 if it was not found -func ArgInArgs(args []string, arg string) int { - for i := 0; i < len(args); i++ { - if args[i] == arg { - return i +func AddTask(tagName string, taskText string) { + if taskText == "" { + fmt.Println("Task text is required.") + os.Exit(1) + } + + if tagName == "" { + Tasks = append(Tasks, ws.Task{Text: taskText}) + return + } + + tag := ws.Tag(tagName) + + // Validate tag + found := false + for _, v := range Tags { + if tag == v { + found = true + break } } - return -1 + if !found { + fmt.Printf("No tag '%s' found.\n", tagName) + os.Exit(1) + } + Tasks = append(Tasks, ws.Task{Text: taskText, Tag: tag}) } -// Parse a number argument from os.Args where pos is the argument's position -// returns the int value for the string, if it fails the program will exit. -// def will be used as default value when there is no argument at the position. If you do not want -// to pass a default value, you can pass -1. -func ParseNArg(pos, def, max int) int { - if max == 0 { - fmt.Printf("Number out of range: %d\n", 0) - os.Exit(1) +func DeleteTask(n int) { + Tasks = append(Tasks[:n-1], Tasks[n:]...) +} + +// save, delete and append task +func ProcrastinateTask(n int) { + t := Tasks[n-1] + Tasks = append(Tasks[:n-1], Tasks[n:]...) + Tasks = append(Tasks, t) +} + +func TopTask(n int) { + var NewTasks []ws.Task + + NewTasks = append(NewTasks, Tasks[n-1]) + NewTasks = append(NewTasks, Tasks[:n-1]...) + if n < len(Tasks) { + NewTasks = append(NewTasks, Tasks[n:]...) } - if len(os.Args) == pos { - if def == -1 { - fmt.Println("Argument required: N") - os.Exit(1) + Tasks = NewTasks +} + +func DoneTask(n int) { + TasksDone = append(TasksDone, ws.TaskDone{ + Task: Tasks[n-1], + Date: time.Now(), + }) + Tasks = append(Tasks[:n-1], Tasks[n:]...) +} + +func UndoneTask(n int) { + Tasks = append(Tasks, TasksDone[n-1].Task) + TasksDone = append(TasksDone[:n-1], TasksDone[n:]...) +} + +func ListTasks(showDone bool) { + if len(Tasks) == 0 && len(TasksDone) == 0 { + fmt.Println("No tasks.") + return + } + + if showDone && len(TasksDone) <= 0 { + showDone = false + } + + if len(Tasks) > 0 { + if showDone { + fmt.Println("Active:") + } + for i, t := range Tasks { + fmt.Printf("% 2d. %s\n", i+1, t) } - return def } - n, err := strconv.Atoi(os.Args[pos]) - if errors.Is(err, strconv.ErrSyntax) { - fmt.Printf("'%s' is not a number.\n", os.Args[pos]) - os.Exit(1) - } else if err != nil { - panic(err) + if showDone { + fmt.Println("Done:") + for i, t := range TasksDone { + fmt.Printf("% 2d. %s\n", i+1, t) + } } +} - if n > max { - fmt.Printf("Number out of range: %d\n", n) - os.Exit(1) +func LsTasks() { + if len(Tasks) == 0 { + fmt.Println("No tasks.") + return + } + for i := 0; i < len(Tasks) && i < ws.TASK_LIST_COUNT; i++ { + fmt.Printf("%2d. %s\n", i+1, Tasks[i]) } - return n +} + +func AddTag(tagName ws.Tag) { + for i := 0; i < len(Tags); i++ { + if tagName == Tags[i] { + fmt.Printf("Tag '%s' already exists.\n", tagName) + os.Exit(1) + } + } + Tags = append(Tags, tagName) +} + +func DeleteTag(n int) { + Tags = append(Tags[:n-1], Tags[n:]...) +} + +func ListTags() { + if len(Tags) == 0 { + fmt.Println("No tags.") + } + for i := 0; i < len(Tags); i++ { + fmt.Printf("%2d. %s\n", i+1, Tags[i]) + } +} + +func wsTagUsage() { + fmt.Println(`usage: ws tag <command> +COMMANDS + add Add a new tag + del Delete a tag + list List tags`) +} + +func wsUsage() { + fmt.Println(`usage: ws <command> [flag] +COMMANDS + task Add a new task + done Mark an active task as done + undone Undo a done task + del Delete an active task + pc Procrastinate an active task + top Put a task to the top of the stack + ls Short list of active tasks + list List all active and done tasks`) } func main() { @@ -100,172 +201,192 @@ func main() { // When no arguments are provided display the first task. if len(os.Args) == 1 { - if len(Tasks) == 0 { - fmt.Println("No tasks.") - return - } - fmt.Printf("1. %s\n", Tasks[0]) + LsTasks() os.Exit(0) } switch os.Args[1] { // Add a new task - case "task": - var tagName, taskText string - var i, offset int - // offset of 2 because we are at the second arg - offset = 2 - i = ArgInArgs(os.Args[offset:], "-t") - - // tag argument was provided - if i != -1 { - if offset+i+1 >= len(os.Args) { - fmt.Println("-t requires an argument.") - os.Exit(1) - } - tagName = os.Args[offset+i+1] - if tagName == "" { - fmt.Println("-t requires an argument.") - os.Exit(1) - } - // this would mean that -t <arg> are the first and last two arguments - if len(os.Args) == offset+2 { - fmt.Println("Task text is required.") - os.Exit(1) - } - if i == 0 { - // tag is at the start - taskText = strings.Join(os.Args[offset+i+2:], " ") - } else if i+4 == len(os.Args) { - // tag is at the end - taskText = strings.Join(os.Args[offset:i+2], " ") - } else { - // tag is in the middle - taskText = strings.Join(append(os.Args[offset:i+2], os.Args[offset+i+2:]...), " ") - } - } else { - taskText = strings.Join(os.Args[offset:], " ") - } + case "add": + var tagName, taskName string - // taskText can be provided as "" which is an argument and will pass previous validation - // tests - if taskText == "" { - fmt.Println("Task text is required.") - os.Exit(1) - } + flagSet := flag.NewFlagSet("add", flag.ExitOnError) + flagSet.StringVar(&tagName, "t", "", "Set tag name") + flagSet.Parse(os.Args[2:]) - if tagName == "" { - Tasks = append(Tasks, ws.Task{Text: taskText}) - break - } + taskName = strings.Join(flagSet.Args(), " ") - tag := ws.Tag(tagName) + AddTask(tagName, taskName) + ListTasks(false) - // Validate tag - found := false - for _, v := range Tags { - if tag == v { - found = true - break - } + // Delete an active task + case "del": + var n int + + flagSet := flag.NewFlagSet("del", flag.ExitOnError) + flagSet.IntVar(&n, "n", 1, "Set task number") + flagSet.Parse(os.Args[2:]) + + if len(Tasks) <= 0 { + fmt.Println("No tasks to delete.") + os.Exit(1) } - if !found { - fmt.Printf("No tag '%s' found.\n", tagName) + + if len(Tasks) < n { + fmt.Println("Task number out of range") os.Exit(1) } - Tasks = append(Tasks, ws.Task{Text: taskText, Tag: tag}) - // Delete an active task - case "del": - n := ParseNArg(2, 1, len(Tasks)) - Tasks = append(Tasks[:n-1], Tasks[n:]...) + DeleteTask(n) + ListTasks(false) // Mark an active task as done case "done": - n := ParseNArg(2, 1, len(Tasks)) - TasksDone = append(TasksDone, ws.TaskDone{ - Task: Tasks[n-1], - Date: time.Now(), - }) - Tasks = append(Tasks[:n-1], Tasks[n:]...) + var n int + + flagSet := flag.NewFlagSet("done", flag.ExitOnError) + flagSet.IntVar(&n, "n", 1, "Set task number") + flagSet.Parse(os.Args[2:]) + + if len(Tasks) <= 0 { + fmt.Println("No tasks to be done.") + os.Exit(1) + } + + if len(Tasks) < n { + fmt.Println("Task number out of range") + os.Exit(1) + } + + DoneTask(n) + ListTasks(true) // Undo a done task case "undone": - n := ParseNArg(2, 1, len(TasksDone)) - Tasks = append(Tasks, TasksDone[n-1].Task) - TasksDone = append(TasksDone[:n-1], TasksDone[n:]...) + var n int + + flagSet := flag.NewFlagSet("undone", flag.ExitOnError) + flagSet.IntVar(&n, "n", 1, "Set task number") + flagSet.Parse(os.Args[2:]) + + if len(TasksDone) <= 0 { + fmt.Println("No tasks to be undone.") + os.Exit(1) + } + + if len(TasksDone) < n { + fmt.Println("Task number out of range") + os.Exit(1) + } + + UndoneTask(n) + ListTasks(true) // Procrastinate an active task case "pc": - n := ParseNArg(2, 1, len(Tasks)) + var n int + + flagSet := flag.NewFlagSet("procrastinate", flag.ExitOnError) + flagSet.IntVar(&n, "n", 1, "Set task number") + flagSet.Parse(os.Args[2:]) + + if len(Tasks) <= 0 { + fmt.Println("No tasks to procrastinate.") + os.Exit(1) + } + + if len(Tasks) < n { + fmt.Println("Task number out of range") + os.Exit(1) + } + + ProcrastinateTask(n) + ListTasks(false) + + // Put a task at the top + case "top": + var n int + + flagSet := flag.NewFlagSet("top", flag.ExitOnError) + flagSet.IntVar(&n, "n", 0, "Set task number") + flagSet.Parse(os.Args[2:]) + + if n == 0 { + fmt.Println("Task number is required.") + os.Exit(1) + } + + if len(Tasks) <= 1 { + fmt.Println("No tasks to put to the top.") + os.Exit(1) + } + if n == 1 { - Tasks = append(Tasks[n:], Tasks[n]) - break + fmt.Println("Task already at the top") + os.Exit(1) + } + + if len(Tasks) < n { + fmt.Println("Task number out of range") + os.Exit(1) } - // save, delete and append task - t := Tasks[n-1] - Tasks = append(Tasks[:n-1], Tasks[n:]...) - Tasks = append(Tasks, t) + + TopTask(n) + ListTasks(false) // Short list of active tasks case "ls": - if len(Tasks) == 0 { - fmt.Println("No tasks.") - break - } - for i := 0; i < len(Tasks) && i < ws.TASK_LIST_COUNT; i++ { - fmt.Printf("%d. %s\n", i+1, Tasks[i]) - } + LsTasks() // List all active and done tasks case "list": - if len(Tasks) > 0 { - fmt.Println("Active:") - for i, t := range Tasks { - fmt.Printf("% 2d. %s\n", i+1, t) - } + ListTasks(true) + + // tag subcommand + case "tag": + if len(os.Args) <= 2 { + wsTagUsage() + return } - if len(TasksDone) > 0 { - fmt.Println("Done:") - for i, t := range TasksDone { - fmt.Printf("% 2d. %s\n", i+1, t) + + switch os.Args[2] { + + // create a new tag + case "add": + tagName := ws.Tag(strings.Join(os.Args[3:], " ")) + AddTag(tagName) + ListTags() + + // delete a tag + case "del": + var n int + + flagSet := flag.NewFlagSet("del", flag.ExitOnError) + flagSet.IntVar(&n, "n", 1, "Set task number") + flagSet.Parse(os.Args[3:]) + + if len(Tags) <= 0 { + fmt.Println("No tags to delete.") + os.Exit(1) } - } - // create a new tag - case "tag": - tagName := ws.Tag(strings.Join(os.Args[2:], " ")) - for i := 0; i < len(Tags); i++ { - if tagName == Tags[i] { - fmt.Printf("Tag '%s' already exists.\n", tagName) + if len(Tags) < n { + fmt.Println("Tag number out of range") os.Exit(1) } - } - Tags = append(Tags, tagName) - // delete a tag - case "tagd": - n := ParseNArg(2, 1, len(Tags)) - Tags = append(Tags[:n-1], Tags[n:]...) + DeleteTag(n) + ListTags() - // list tags - case "tagl": - for i := 0; i < len(Tags); i++ { - fmt.Printf("%2d. %s\n", i+1, Tags[i]) + /* list tags */ + case "list": + ListTags() + default: + wsTagUsage() } + default: - fmt.Println(`usage: ws <command> -COMMANDS - task Add a new task - done Mark an active task as done - undone Undo a done task - del Delete an active task - pc Procrastinate an active task - ls Short list of active tasks - list List all active and done tasks - tag Add a new tag - tagd Delete a tag - tagl List tags`) + wsUsage() } // Save data to gobdata |