package main import ( "encoding/gob" "errors" "flag" "fmt" "os" "strings" "time" ws "git.spacehb.net/ws.git" ) var ( // Stack of active tasks Tags []ws.Tag // Active tasks Tasks []ws.Task // Completed tasks TasksDone []ws.TaskDone // Persistent storage for Tasks ) 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 } } if !found { fmt.Printf("No tag '%s' found.\n", tagName) os.Exit(1) } Tasks = append(Tasks, ws.Task{Text: taskText, Tag: tag}) } 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:]...) } 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) } } if showDone { fmt.Println("Done:") for i, t := range TasksDone { fmt.Printf("% 2d. %s\n", i+1, t) } } } 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]) } } 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 COMMANDS add Add a new tag del Delete a tag list List tags`) } func wsUsage() { fmt.Println(`usage: ws [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() { var ( dec *gob.Decoder f *os.File err error gobdataPath string = ws.GetGobdataPath() ) // Open/Create gob data file if not exist { f, err = os.Open(gobdataPath) if errors.Is(err, os.ErrNotExist) { // Do nothing } else if err != nil { panic(err) } else { dec = gob.NewDecoder(f) err = dec.Decode(&Tasks) if err != nil { panic(err) } err = dec.Decode(&TasksDone) if err != nil { panic(err) } err = dec.Decode(&Tags) if err != nil { panic(err) } } } // When no arguments are provided display the first task. if len(os.Args) == 1 { LsTasks() os.Exit(0) } switch os.Args[1] { // Add a new task case "add": var tagName, taskName string flagSet := flag.NewFlagSet("add", flag.ExitOnError) flagSet.StringVar(&tagName, "t", "", "Set tag name") flagSet.Parse(os.Args[2:]) taskName = strings.Join(flagSet.Args(), " ") AddTask(tagName, taskName) ListTasks(false) // 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 len(Tasks) < n { fmt.Println("Task number out of range") os.Exit(1) } DeleteTask(n) ListTasks(false) // Mark an active task as done case "done": 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": 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": 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 { fmt.Println("Task already at the top") os.Exit(1) } if len(Tasks) < n { fmt.Println("Task number out of range") os.Exit(1) } TopTask(n) ListTasks(false) // Short list of active tasks case "ls": LsTasks() // List all active and done tasks case "list": ListTasks(true) // tag subcommand case "tag": if len(os.Args) <= 2 { wsTagUsage() return } 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) } if len(Tags) < n { fmt.Println("Tag number out of range") os.Exit(1) } DeleteTag(n) ListTags() /* list tags */ case "list": ListTags() default: wsTagUsage() } default: wsUsage() } // Save data to gobdata f, err = os.Create(gobdataPath) if err != nil { panic(err) } enc := gob.NewEncoder(f) err = enc.Encode(Tasks) if err != nil { panic(err) } err = enc.Encode(TasksDone) if err != nil { panic(err) } err = enc.Encode(Tags) if err != nil { panic(err) } }