Implement task management commands: add, list, done, undo, remove
This commit is contained in:
308
main.go
308
main.go
@@ -38,15 +38,118 @@ func init() {
|
|||||||
log.Fatal("Unable to ping database:", err)
|
log.Fatal("Unable to ping database:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Connected to PostgreSQL database!")
|
// fmt.Println("Connected to PostgreSQL database!")
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app := &cli.App{
|
app := &cli.App{
|
||||||
Name: "Go Todo App",
|
Name: "gotodo",
|
||||||
Usage: "A simple CLI program to manage your tasks",
|
Usage: "A simple CLI program to manage your tasks",
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
tasks, err := getPendingTasks()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Pending Tasks:")
|
||||||
|
printTasks(tasks)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
Commands: []*cli.Command{
|
Commands: []*cli.Command{
|
||||||
// We'll add commands here
|
{
|
||||||
|
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 createTask(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 []Task
|
||||||
|
var err error
|
||||||
|
title := "Pending Tasks:"
|
||||||
|
|
||||||
|
if c.Bool("all") {
|
||||||
|
tasks, err = getAllTasks()
|
||||||
|
title = "All Tasks:"
|
||||||
|
} else if c.Bool("completed") {
|
||||||
|
tasks, err = getCompletedTasks()
|
||||||
|
title = "Completed Tasks:"
|
||||||
|
} else {
|
||||||
|
tasks, err = getPendingTasks()
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
return completeTask(id)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
return undoTask(id)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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 deleteTask(id)
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,3 +158,202 @@ func main() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createTask(text string) error {
|
||||||
|
sql := `
|
||||||
|
INSERT INTO tasks (text, completed)
|
||||||
|
VALUES ($1, $2)
|
||||||
|
RETURNING id
|
||||||
|
`
|
||||||
|
|
||||||
|
var id int
|
||||||
|
err := pool.QueryRow(ctx, sql, text, false).Scan(&id)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error creating task: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Created task with ID: %d\n", id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAllTasks() ([]Task, error) {
|
||||||
|
sql := `
|
||||||
|
SELECT id, text, completed, created_at, updated_at
|
||||||
|
FROM tasks
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
rows, err := pool.Query(ctx, sql)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error querying tasks: %w", err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var tasks []Task
|
||||||
|
for rows.Next() {
|
||||||
|
var task Task
|
||||||
|
err := rows.Scan(
|
||||||
|
&task.Id,
|
||||||
|
&task.Text,
|
||||||
|
&task.Completed,
|
||||||
|
&task.CreatedAt,
|
||||||
|
&task.UpdatedAt,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error scanning task row: %w", err)
|
||||||
|
}
|
||||||
|
tasks = append(tasks, task)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, fmt.Errorf("error iterating task rows: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tasks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func completeTask(id int) error {
|
||||||
|
sql := `
|
||||||
|
UPDATE tasks
|
||||||
|
SET completed = true, updated_at = NOW()
|
||||||
|
WHERE id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
commandTag, err := pool.Exec(ctx, sql, id)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error completing task: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if commandTag.RowsAffected() == 0 {
|
||||||
|
return fmt.Errorf("no task found with id %d", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func undoTask(id int) error {
|
||||||
|
sql := `
|
||||||
|
UPDATE tasks
|
||||||
|
SET completed = false, updated_at = NOW()
|
||||||
|
WHERE id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
commandTag, err := pool.Exec(ctx, sql, id)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error updating task: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if commandTag.RowsAffected() == 0 {
|
||||||
|
return fmt.Errorf("no task found with id %d", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteTask(id int) error {
|
||||||
|
sql := `DELETE FROM tasks WHERE id = $1`
|
||||||
|
|
||||||
|
commandTag, err := pool.Exec(ctx, sql, id)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error deleting task: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if commandTag.RowsAffected() == 0 {
|
||||||
|
return fmt.Errorf("no task found with id %d", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPendingTasks() ([]Task, error) {
|
||||||
|
sql := `
|
||||||
|
SELECT id, text, completed, created_at, updated_at
|
||||||
|
FROM tasks
|
||||||
|
WHERE completed = false
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
rows, err := pool.Query(ctx, sql)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error querying pending tasks: %w", err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var tasks []Task
|
||||||
|
for rows.Next() {
|
||||||
|
var task Task
|
||||||
|
err := rows.Scan(
|
||||||
|
&task.Id,
|
||||||
|
&task.Text,
|
||||||
|
&task.Completed,
|
||||||
|
&task.CreatedAt,
|
||||||
|
&task.UpdatedAt,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error scanning task row: %w", err)
|
||||||
|
}
|
||||||
|
tasks = append(tasks, task)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, fmt.Errorf("error iterating task rows: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tasks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCompletedTasks() ([]Task, error) {
|
||||||
|
sql := `
|
||||||
|
SELECT id, text, completed, created_at, updated_at
|
||||||
|
FROM tasks
|
||||||
|
WHERE completed = true
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
rows, err := pool.Query(ctx, sql)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error querying completed tasks: %w", err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var tasks []Task
|
||||||
|
for rows.Next() {
|
||||||
|
var task Task
|
||||||
|
err := rows.Scan(
|
||||||
|
&task.Id,
|
||||||
|
&task.Text,
|
||||||
|
&task.Completed,
|
||||||
|
&task.CreatedAt,
|
||||||
|
&task.UpdatedAt,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error scanning task row: %w", err)
|
||||||
|
}
|
||||||
|
tasks = append(tasks, task)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, fmt.Errorf("error iterating task rows: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tasks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func printTasks(tasks []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"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user