├── LICENSE ├── README.md ├── command.go ├── go.mod ├── go.sum ├── main.go ├── storage.go ├── todo.exe ├── todo.go └── todos.json /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Yash Kamble 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Todo-CLI-Go 2 | 3 | 4 | 5 | Todo-CLI-Go is a command-line interface (CLI) tool written in Go for managing a simple to-do list. It allows users to add, list, edit, and toggle tasks directly from the command line. 6 | 7 | ## Setup 8 | 9 | Follow these steps to get your Todo-CLI-Go project up and running: 10 | 11 | 1. **Clone the Repository** 12 | 13 | ```bash 14 | git clone https://github.com/hackice20/todo-cli-go.git 15 | ``` 16 | 17 | 2. **Go Setup** 18 | 19 | Navigate to the project directory: 20 | 21 | ```bash 22 | cd todo-cli-go 23 | ``` 24 | 25 | Ensure you have Go installed. If not, download and install it from [the Go website](https://golang.org/dl/). 26 | 27 | 3. **Build the Project** 28 | 29 | Build the project using the Go build command: 30 | 31 | ```bash 32 | go build -o todo 33 | ``` 34 | 35 | This will create an executable file named `todo` in the project directory. 36 | 37 | ## CLI Commands 38 | 39 | Here are the commands you can use with the Todo-CLI-Go tool: 40 | 41 | | Command | Description | 42 | |---------------------------------|----------------------------------| 43 | | `./todo -add "task"` | Add a new to-do item | 44 | | `./todo -list` | List all to-do items | 45 | | `./todo toggle ` | Mark a to-do item as done | 46 | | `./todo edit " new_name"` | Edit the name of an existing to-do item | 47 | 48 | ## Example Usage 49 | 50 | 1. **Add a new to-do item:** 51 | 52 | ```bash 53 | ./todo -add "Buy groceries" 54 | ``` 55 | 56 | 2. **List all to-do items:** 57 | 58 | ```bash 59 | ./todo -list 60 | ``` 61 | 62 | 3. **Mark a to-do item as done:** 63 | 64 | ```bash 65 | ./todo toggle 1 66 | ``` 67 | 68 | 4. **Edit a to-do item:** 69 | 70 | ```bash 71 | ./todo edit "1 Buy groceries and more" 72 | ``` 73 | 74 | 75 | -------------------------------------------------------------------------------- /command.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | type CmdFlags struct { 12 | Add string 13 | Del int 14 | Edit string 15 | Toggle int 16 | List bool 17 | } 18 | 19 | func NewCmdFlags() *CmdFlags { 20 | cf := CmdFlags{} 21 | 22 | flag.StringVar(&cf.Add, "add", "", "Add a new todo specify title") 23 | flag.StringVar(&cf.Edit, "edit", "", "Edit a todo by index & specify a new title. id:new_title") 24 | flag.IntVar(&cf.Del, "del", -1, "Specify a todo by index to delete") 25 | flag.IntVar(&cf.Toggle, "toggle", -1, "Specify a todo by index to toggle") 26 | flag.BoolVar(&cf.List, "list", false, "List all todos") 27 | 28 | flag.Parse() 29 | 30 | return &cf 31 | } 32 | 33 | func (cf *CmdFlags) Execute(todos *Todos) { 34 | switch { 35 | case cf.List: 36 | todos.print() 37 | case cf.Add != "": 38 | todos.add(cf.Add) 39 | case cf.Edit != "": 40 | parts := strings.SplitN(cf.Edit, ":", 2) 41 | if len(parts) != 2 { 42 | fmt.Println("Error, invalid format for edit. Please use id:new_title") 43 | os.Exit(1) 44 | } 45 | 46 | index, err := strconv.Atoi(parts[0]) 47 | 48 | if err != nil { 49 | fmt.Println("Error: invalid index for edit") 50 | os.Exit(1) 51 | } 52 | 53 | todos.edit(index, parts[1]) 54 | 55 | case cf.Toggle != -1: 56 | todos.toggle(cf.Toggle) 57 | 58 | case cf.Del != -1: 59 | todos.delete(cf.Del) 60 | 61 | default: 62 | fmt.Println("Invalid command") 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module todo 2 | 3 | go 1.22.3 4 | 5 | require ( 6 | github.com/aquasecurity/table v1.8.0 // indirect 7 | github.com/mattn/go-runewidth v0.0.13 // indirect 8 | github.com/rivo/uniseg v0.2.0 // indirect 9 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect 10 | golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect 11 | ) -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/aquasecurity/table v1.8.0 h1:9ntpSwrUfjrM6/YviArlx/ZBGd6ix8W+MtojQcM7tv0= 2 | github.com/aquasecurity/table v1.8.0/go.mod h1:eqOmvjjB7AhXFgFqpJUEE/ietg7RrMSJZXyTN8E/wZw= 3 | github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= 4 | github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 5 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 6 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 7 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= 8 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 9 | golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= 10 | golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | todos := Todos{} 5 | storage := NewStorage[Todos]("todos.json") 6 | storage.Load(&todos) 7 | cmdFlags := NewCmdFlags() 8 | cmdFlags.Execute(&todos) 9 | storage.Save(todos) 10 | } 11 | -------------------------------------------------------------------------------- /storage.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | ) 7 | 8 | type Storage[T any] struct { 9 | FileName string 10 | } 11 | 12 | func NewStorage[T any](fileName string) *Storage[T] { 13 | return &Storage[T]{FileName: fileName} 14 | } 15 | 16 | func (s *Storage[T]) Save(data T) error { 17 | fileData, err := json.MarshalIndent(data, "", " ") 18 | 19 | if err != nil { 20 | return err 21 | } 22 | 23 | return os.WriteFile(s.FileName, fileData, 0644) 24 | } 25 | 26 | func (s *Storage[T]) Load(data *T) error { 27 | fileData, err := os.ReadFile(s.FileName) 28 | 29 | if err != nil { 30 | return err 31 | } 32 | 33 | return json.Unmarshal(fileData, data) 34 | } 35 | -------------------------------------------------------------------------------- /todo.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackice20/todo-cli-go/ac8a89287f20b745c12ce1842eaa3f2681017140/todo.exe -------------------------------------------------------------------------------- /todo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | "time" 9 | 10 | "github.com/aquasecurity/table" 11 | ) 12 | 13 | type Todo struct { 14 | Title string 15 | Completed bool 16 | CreatedAt time.Time 17 | CompletedAt *time.Time 18 | } 19 | 20 | type Todos []Todo 21 | 22 | func (todos *Todos) add(title string) { 23 | todo := Todo{ 24 | Title: title, 25 | Completed: false, 26 | CompletedAt: nil, 27 | CreatedAt: time.Now(), 28 | } 29 | 30 | *todos = append(*todos, todo) 31 | } 32 | 33 | func (todos *Todos) validateIndex(index int) error { 34 | if index < 0 || index >= len(*todos) { 35 | err := errors.New("invalid index") 36 | fmt.Println(err) 37 | return err 38 | } 39 | 40 | return nil 41 | } 42 | 43 | func (todos *Todos) delete(index int) error { 44 | t := *todos 45 | 46 | if err := t.validateIndex(index); err != nil { 47 | return err 48 | } 49 | 50 | *todos = append(t[:index], t[index+1:]...) 51 | 52 | return nil 53 | } 54 | 55 | func (todos *Todos) toggle(index int) error { 56 | t := *todos 57 | 58 | if err := t.validateIndex(index); err != nil { 59 | return err 60 | } 61 | 62 | isCompleted := t[index].Completed 63 | 64 | if !isCompleted { 65 | completionTime := time.Now() 66 | t[index].CompletedAt = &completionTime 67 | } 68 | 69 | t[index].Completed = !isCompleted 70 | 71 | return nil 72 | } 73 | 74 | func (todos *Todos) edit(index int, title string) error { 75 | t := *todos 76 | 77 | if err := t.validateIndex(index); err != nil { 78 | return err 79 | } 80 | 81 | t[index].Title = title 82 | 83 | return nil 84 | } 85 | 86 | func (todos *Todos) print() { 87 | table := table.New(os.Stdout) 88 | table.SetRowLines(false) 89 | table.SetHeaders("#", "Title", "Completed", "Created At", "Completed At") 90 | for index, t := range *todos { 91 | completed := "❌" 92 | completedAt := "" 93 | 94 | if t.Completed { 95 | completed = "✅" 96 | if t.CompletedAt != nil { 97 | completedAt = t.CompletedAt.Format(time.RFC1123) 98 | } 99 | } 100 | 101 | table.AddRow(strconv.Itoa(index), t.Title, completed, t.CreatedAt.Format(time.RFC1123), completedAt) 102 | } 103 | 104 | table.Render() 105 | } 106 | -------------------------------------------------------------------------------- /todos.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Title": "doing ur mom if u fork my repo", 4 | "Completed": false, 5 | "CreatedAt": "2024-08-19T08:37:31.8293814+05:30", 6 | "CompletedAt": null 7 | }, 8 | { 9 | "Title": "build a better rocket than SpaceX and reach Mars", 10 | "Completed": false, 11 | "CreatedAt": "2024-08-19T08:38:19.5612635+05:30", 12 | "CompletedAt": null 13 | }, 14 | { 15 | "Title": "doing ur mom anyways", 16 | "Completed": true, 17 | "CreatedAt": "2024-08-19T08:39:26.1105243+05:30", 18 | "CompletedAt": "2024-08-19T08:58:29.1462971+05:30" 19 | }, 20 | { 21 | "Title": "study", 22 | "Completed": false, 23 | "CreatedAt": "2024-08-19T08:49:38.6787187+05:30", 24 | "CompletedAt": null 25 | } 26 | ] --------------------------------------------------------------------------------