package main import ( "context" "fmt" "log" "os" "git.ksdemosapps.com/kylesoda/pgx-learning/internal/config" "git.ksdemosapps.com/kylesoda/pgx-learning/internal/db/postgres" "git.ksdemosapps.com/kylesoda/pgx-learning/internal/models" "git.ksdemosapps.com/kylesoda/pgx-learning/internal/repository" "github.com/jackc/pgx/v5/pgxpool" "github.com/urfave/cli/v2" ) var pool *pgxpool.Pool var ctx = context.Background() func init() { var err error pool, err = pgxpool.New(ctx, config.Db.Url) if err != nil { log.Fatal("Unable to connect to database:", err) } if err := pool.Ping(ctx); err != nil { log.Fatal("Unable to ping database:", err) } // fmt.Println("Connected to PostgreSQL database!") } func main() { taskRespository := postgres.NewTaskRepository(pool) app := &cli.App{ Name: "gotodo", Usage: "A simple CLI program to manage your tasks", Action: func(c *cli.Context) error { completedState := false tasks, err := taskRespository.GetAll(ctx, repository.GetAllTaskFilters{ Limit: 100, Completed: &completedState, }) if err != nil { return err } fmt.Println("Pending Tasks:") printTasks(tasks) return nil }, Commands: []*cli.Command{ { Name: "add", Aliases: []string{"a"}, Usage: "Add a new task", Action: func(c *cli.Context) error { text := c.Args().First() if text == "" { return fmt.Errorf("task text cannot be empty") } return taskRespository.Save(ctx, &models.Task{ Text: text, }) }, }, { Name: "list", Aliases: []string{"ls"}, Usage: "List tasks with filters", Flags: []cli.Flag{ &cli.BoolFlag{Name: "all", Aliases: []string{"a"}, Usage: "List all tasks"}, &cli.BoolFlag{Name: "completed", Aliases: []string{"c"}, Usage: "List only completed tasks"}, &cli.BoolFlag{Name: "pending", Aliases: []string{"p"}, Usage: "List only pending tasks"}, }, Action: func(c *cli.Context) error { var tasks []models.Task var err error title := "Pending Tasks:" if c.Bool("all") { tasks, err = taskRespository.GetAll(ctx, repository.GetAllTaskFilters{ Limit: 100, }) title = "All Tasks:" } else if c.Bool("completed") { completedState := true tasks, err = taskRespository.GetAll(ctx, repository.GetAllTaskFilters{ Limit: 100, Completed: &completedState, }) title = "Completed Tasks:" } else { completedState := false tasks, err = taskRespository.GetAll(ctx, repository.GetAllTaskFilters{ Limit: 100, Completed: &completedState, }) title = "Pending Tasks:" } if err != nil { return err } fmt.Println(title) printTasks(tasks) return nil }, }, { Name: "done", Aliases: []string{"do"}, Usage: "Mark a task as completed", Action: func(c *cli.Context) error { idStr := c.Args().First() if idStr == "" { return fmt.Errorf("task ID required") } var id int if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil { return fmt.Errorf("invalid task ID: %s", idStr) } completedState := true _, err := taskRespository.Update(ctx, id, &models.UpdateTaskInput{ Completed: &completedState, }) return err }, }, { Name: "undo", Aliases: []string{"ud"}, Usage: "Mark a task as not completed", Action: func(c *cli.Context) error { idStr := c.Args().First() if idStr == "" { return fmt.Errorf("task ID required") } var id int if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil { return fmt.Errorf("invalid task ID: %s", idStr) } completedState := false _, err := taskRespository.Update(ctx, id, &models.UpdateTaskInput{ Completed: &completedState, }) return err }, }, { Name: "remove", Aliases: []string{"rm"}, Usage: "Remove a task", Action: func(c *cli.Context) error { idStr := c.Args().First() if idStr == "" { return fmt.Errorf("task ID required") } var id int if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil { return fmt.Errorf("invalid task ID: %s", idStr) } return taskRespository.Delete(ctx, id) }, }, }, } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } func printTasks(tasks []models.Task) { if len(tasks) == 0 { fmt.Println("No tasks found") return } for _, task := range tasks { status := "[ ]" if task.Completed { status = "[✓]" } fmt.Printf("%d. %s %s (Created: %s)\n", task.Id, status, task.Text, task.CreatedAt.Format("2006-01-02 15:04:05")) } }