The template's files at a77fd30, without its history or LICENSE, after script/rename simplexcalc. Model: opus-5-5
107 lines
2.6 KiB
Go
107 lines
2.6 KiB
Go
package database
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
// Go has no UUID in the standard library as of go1.25 — checked
|
|
// against this repo's toolchain, not assumed. Swap this import for
|
|
// the stdlib package the moment one lands; nothing else here
|
|
// depends on the implementation.
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// timeFormat matches the strftime pattern the schema uses for its
|
|
// defaults, so rows written by Go and rows written by a DEFAULT sort
|
|
// against each other correctly.
|
|
const timeFormat = "2006-01-02T15:04:05.000Z"
|
|
|
|
// Widget is the example row type. It exists so that the migration
|
|
// runner, the query layer, the templates and the tests all exercise
|
|
// real data. Delete it when seeding a real project.
|
|
type Widget struct {
|
|
ID string
|
|
Name string
|
|
SizeBytes int64
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
// CreateWidget inserts a widget and returns it as stored.
|
|
func (d *Database) CreateWidget(
|
|
ctx context.Context, name string, size int64,
|
|
) (*Widget, error) {
|
|
w := &Widget{
|
|
ID: uuid.NewString(),
|
|
Name: name,
|
|
SizeBytes: size,
|
|
CreatedAt: time.Now().UTC(),
|
|
}
|
|
|
|
_, err := d.db.ExecContext(ctx,
|
|
`INSERT INTO widgets (id, name, size_bytes, created_at) VALUES (?, ?, ?, ?)`,
|
|
w.ID, w.Name, w.SizeBytes, w.CreatedAt.Format(timeFormat),
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("inserting widget: %w", err)
|
|
}
|
|
|
|
return w, nil
|
|
}
|
|
|
|
// ListWidgets returns the most recently created widgets, newest first,
|
|
// up to limit.
|
|
func (d *Database) ListWidgets(ctx context.Context, limit int) ([]Widget, error) {
|
|
rows, err := d.db.QueryContext(ctx,
|
|
`SELECT id, name, size_bytes, created_at
|
|
FROM widgets
|
|
ORDER BY created_at DESC, id DESC
|
|
LIMIT ?`,
|
|
limit,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("listing widgets: %w", err)
|
|
}
|
|
defer func() { _ = rows.Close() }()
|
|
|
|
widgets := []Widget{}
|
|
|
|
for rows.Next() {
|
|
var (
|
|
w Widget
|
|
createdAt string
|
|
)
|
|
|
|
scanErr := rows.Scan(&w.ID, &w.Name, &w.SizeBytes, &createdAt)
|
|
if scanErr != nil {
|
|
return nil, fmt.Errorf("scanning widget: %w", scanErr)
|
|
}
|
|
|
|
w.CreatedAt, scanErr = time.Parse(timeFormat, createdAt)
|
|
if scanErr != nil {
|
|
return nil, fmt.Errorf("parsing widget created_at %q: %w", createdAt, scanErr)
|
|
}
|
|
|
|
widgets = append(widgets, w)
|
|
}
|
|
|
|
err = rows.Err()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("iterating widgets: %w", err)
|
|
}
|
|
|
|
return widgets, nil
|
|
}
|
|
|
|
// CountWidgets returns the number of widgets stored.
|
|
func (d *Database) CountWidgets(ctx context.Context) (int, error) {
|
|
var n int
|
|
|
|
err := d.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM widgets`).Scan(&n)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("counting widgets: %w", err)
|
|
}
|
|
|
|
return n, nil
|
|
}
|