diff --git a/Go/.gitignore b/Go/.gitignore new file mode 100644 index 0000000..dcd6fb3 --- /dev/null +++ b/Go/.gitignore @@ -0,0 +1,30 @@ +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work +go.work.sum +go.sum +go.mod + +# env file +.env + +# json files +todos.json \ No newline at end of file diff --git a/Go/command.go b/Go/command.go new file mode 100644 index 0000000..e45b0f3 --- /dev/null +++ b/Go/command.go @@ -0,0 +1,67 @@ +package main + +import ( + "flag" + "fmt" + "os" + "strconv" + "strings" +) + +type CommandFlags struct { + Add string + Delete int + Edit string + Toggle int + List bool +} + +func NewCommandFlags() *CommandFlags { + commandFlag := CommandFlags{} + + flag.StringVar(&commandFlag.Add, "add", "", "Add a new todo specify title") + flag.StringVar(&commandFlag.Edit, "edit", "", "Edit a todo by index & specify a new title. id:new_title") + flag.IntVar(&commandFlag.Delete, "delete", -1, "Specify a todo by index to delete") + flag.IntVar(&commandFlag.Toggle, "toggle", -1, "Specify a todo by index to toggle") + flag.BoolVar(&commandFlag.List, "list", false, "List all todos") + + flag.Parse() + + return &commandFlag +} + +func (commandFlag *CommandFlags) Execute(todos *Todos) { + switch { + case commandFlag.List: + todos.print() + + case commandFlag.Add != "": + todos.add(commandFlag.Add) + + case commandFlag.Edit != "": + parts := strings.SplitN(commandFlag.Edit, ":", 2) + if len(parts) != 2 { + fmt.Println("Error! Invalid format for edit. Please use id:new_title") + os.Exit(1) + } + + index, err := strconv.Atoi(parts[0]) + + if err != nil { + fmt.Println("Error! Invalid index for edit!") + os.Exit(1) + } + + todos.edit(index, parts[1]) + + case commandFlag.Toggle != -1: + todos.toggle(commandFlag.Toggle) + + case commandFlag.Delete != -1: + todos.delete(commandFlag.Delete) + + default: + fmt.Println("Invalid command!") + } + +} diff --git a/Go/main.go b/Go/main.go new file mode 100644 index 0000000..a3efe5f --- /dev/null +++ b/Go/main.go @@ -0,0 +1,10 @@ +package main + +func main() { + todos := Todos{} + storage := NewStorage[Todos]("todos.json") + storage.Load(&todos) + commandFlags := NewCommandFlags() + commandFlags.Execute(&todos) + storage.Save(todos) +} diff --git a/Go/storage.go b/Go/storage.go new file mode 100644 index 0000000..db1dede --- /dev/null +++ b/Go/storage.go @@ -0,0 +1,34 @@ +package main + +import ( + "encoding/json" + "os" +) + +type Storage[T any] struct { + FileName string +} + +func NewStorage[T any](fileName string) *Storage[T] { + return &Storage[T]{FileName: fileName} +} + +func (storage *Storage[T]) Save(data T) error { + fileData, err := json.MarshalIndent(data, "", " ") + + if err != nil { + return err + } + + return os.WriteFile(storage.FileName, fileData, 0644) +} + +func (storage *Storage[T]) Load(data *T) error { + fileData, err := os.ReadFile(storage.FileName) + + if err != nil { + return err + } + + return json.Unmarshal(fileData, data) +} diff --git a/Go/todo.go b/Go/todo.go new file mode 100644 index 0000000..3fa1ac8 --- /dev/null +++ b/Go/todo.go @@ -0,0 +1,107 @@ +package main + +import ( + "errors" + "fmt" + "os" + "strconv" + "time" + + "github.com/aquasecurity/table" +) + +type Todo struct { + Title string + Completed bool + CreatedAt time.Time + CompletedAt *time.Time +} + +type Todos []Todo + +func (todos *Todos) add(title string) { + todo := Todo{ + Title: title, + Completed: false, + CompletedAt: nil, + CreatedAt: time.Now(), + } + + *todos = append(*todos, todo) +} + +func (todos *Todos) validateIndex(index int) error { + if index < 0 || index >= len(*todos) { + err := errors.New("Invalid Index") + fmt.Println(err) + return err + } + + return nil +} + +func (todos *Todos) delete(index int) error { + t := *todos + + if err := t.validateIndex(index); err != nil { + return err + } + + *todos = append(t[:index], t[index+1:]...) + + return nil +} + +func (todos *Todos) toggle(index int) error { + t := *todos + + if err := t.validateIndex(index); err != nil { + return err + } + + isCompleted := t[index].Completed + + if !isCompleted { + completionTime := time.Now() + t[index].CompletedAt = &completionTime + } + + t[index].Completed = !isCompleted + + return nil +} + +func (todos *Todos) edit(index int, title string) error { + t := *todos + + if err := t.validateIndex(index); err != nil { + return err + } + + t[index].Title = title + + return nil +} + +func (todos *Todos) print() { + table := table.New(os.Stdout) + table.SetRowLines(false) + table.SetHeaders("#", "Title", "Completed", "Created At", "Completed At") + + for index, t := range *todos { + completed := "❌" + completedAt := "" + + if t.Completed { + completed = "✔️" + if t.CompletedAt != nil { + completedAt = t.CompletedAt.Format(time.RFC1123) + } + } + + table.AddRow(strconv.Itoa(index), t.Title, completed, + t.CreatedAt.Format(time.RFC1123), completedAt) + } + + table.Render() +}