package repository import ( "context" "code.nochebuena.dev/go/sqlite" "code.nochebuena.dev/go/todo-api/internal/domain" ) // TodoRepository defines persistence operations for todos. type TodoRepository interface { FindAll(ctx context.Context) ([]domain.Todo, error) Create(ctx context.Context, todo domain.Todo) (domain.Todo, error) } type todoRepository struct { db sqlite.Client } // NewTodoRepository returns a SQLite-backed TodoRepository. func NewTodoRepository(db sqlite.Client) TodoRepository { return &todoRepository{db: db} } func (r *todoRepository) FindAll(ctx context.Context) ([]domain.Todo, error) { rows, err := r.db.GetExecutor(ctx).QueryContext(ctx, `SELECT id, title, done, created_at FROM todos ORDER BY created_at DESC`, ) if err != nil { return nil, r.db.HandleError(err) } defer rows.Close() var todos []domain.Todo for rows.Next() { var t domain.Todo if err := rows.Scan(&t.ID, &t.Title, &t.Done, &t.CreatedAt); err != nil { return nil, err } todos = append(todos, t) } if err := rows.Err(); err != nil { return nil, err } if todos == nil { todos = []domain.Todo{} // always return a slice, never nil } return todos, nil } func (r *todoRepository) Create(ctx context.Context, todo domain.Todo) (domain.Todo, error) { row := r.db.GetExecutor(ctx).QueryRowContext(ctx, `INSERT INTO todos (title, done) VALUES (?, ?) RETURNING id, title, done, created_at`, todo.Title, todo.Done, ) var t domain.Todo if err := row.Scan(&t.ID, &t.Title, &t.Done, &t.CreatedAt); err != nil { return domain.Todo{}, r.db.HandleError(err) } return t, nil }