feat(database): make all database operation be methods instead of methods+structs

This is inspired by the output code generated by sqlc
This commit is contained in:
Guz
2025-03-12 10:15:38 -03:00
parent 9fbcbb96c0
commit 71cd17bb97
2 changed files with 116 additions and 43 deletions

View File

@@ -4,12 +4,13 @@ import (
"context"
"database/sql"
"errors"
"fmt"
"log/slog"
"forge.capytal.company/loreddev/x/tinyssert"
)
var ErrNoRows = sql.ErrNoRows
type Database struct {
sql *sql.DB
ctx context.Context
@@ -72,19 +73,19 @@ func (db *Database) setup() error {
log.Debug("Creating tables")
tables := []Table{
&Project{},
}
tx, err := db.sql.BeginTx(db.ctx, nil)
if err != nil {
return errors.Join(errors.New("unable to start transaction to create tables"), err)
}
for _, t := range tables {
_, err := tx.Exec(t.setup())
setups := []func(*sql.Tx) error{
db.setupProjects,
}
for _, setup := range setups {
err := setup(tx)
if err != nil {
return errors.Join(fmt.Errorf("error while trying to create table %T", t), err)
return err
}
}
@@ -95,21 +96,3 @@ func (db *Database) setup() error {
return nil
}
func (db *Database) Insert(t Table) error {
q, err := t.insert()
if err != nil {
return errors.Join(fmt.Errorf("creating query to insert table %T resulted in error", t), err)
}
_, err = db.sql.ExecContext(db.ctx, q)
if err != nil {
return err
}
return nil
}
type Table interface {
setup() string
insert() (string, error)
}

View File

@@ -1,8 +1,10 @@
package database
import (
"database/sql"
"errors"
"fmt"
"log/slog"
)
type Project struct {
@@ -10,29 +12,117 @@ type Project struct {
Title string
}
var _ Table = (*Project)(nil)
func (db *Database) setupProjects(tx *sql.Tx) error {
db.assert.NotNil(tx)
db.assert.NotNil(db.ctx)
func (p *Project) setup() string {
return `
CREATE TABLE IF NOT EXISTS projects (
q := `CREATE TABLE IF NOT EXISTS projects (
id TEXT PRIMARY KEY NOT NULL,
title TEXT NOT NULL
) STRICT`
_, err := tx.ExecContext(db.ctx, q)
if err != nil {
return errors.Join(errors.New(`unable to execute create query to table "projects"`), err)
}
return nil
}
func (p *Project) insert() (string, error) {
if p.ID == "" {
return "", errors.New("ID field shouldn't be a empty string")
func (db *Database) CreateProject(id string, title string) (Project, error) {
db.assert.NotNil(db.sql)
db.assert.NotNil(db.ctx)
db.assert.NotNil(db.log)
db.assert.NotZero(id)
db.assert.NotZero(title)
q := fmt.Sprintf(`INSERT INTO projects (id, title) VALUES ('%s', '%s')`, id, title)
db.log.Debug("Inserting into Projects", slog.String("query", q))
tx, err := db.sql.BeginTx(db.ctx, nil)
if err != nil {
return Project{}, err
}
if p.Title == "" {
return "", errors.New("Title field shouldn't be a empty string")
_, err = tx.ExecContext(db.ctx, q)
if err != nil {
return Project{}, err
}
return fmt.Sprintf(`
INSERT OR FAIL INTO projects (
id,
title
) VALUES (
'%s',
'%s'
)`, p.ID, p.Title), nil
err = tx.Commit()
if err != nil {
return Project{}, err
}
return Project{ID: id, Title: title}, nil
}
func (db *Database) GetProject(id string) (Project, error) {
db.assert.NotNil(db.sql)
db.assert.NotNil(db.ctx)
db.assert.NotNil(db.log)
q := fmt.Sprintf(`SELECT id, title FROM projects WHERE id = '%s'`, id)
db.log.Debug("Getting Project", slog.String("query", q))
tx, err := db.sql.BeginTx(db.ctx, nil)
if err != nil {
return Project{}, err
}
row := tx.QueryRowContext(db.ctx, q)
p := Project{}
err = row.Scan(&p.ID, &p.Title)
if err != nil {
return p, err
}
err = tx.Commit()
if err != nil {
return p, err
}
return p, nil
}
func (db *Database) ListProjects() ([]Project, error) {
db.assert.NotNil(db.sql)
db.assert.NotNil(db.ctx)
db.assert.NotNil(db.log)
q := `SELECT id, title FROM projects`
db.log.Debug("Listing Projects", slog.String("query", q))
tx, err := db.sql.BeginTx(db.ctx, nil)
if err != nil {
return []Project{}, err
}
rows, err := tx.QueryContext(db.ctx, q)
if err != nil {
db.assert.Nil(tx.Rollback())
return []Project{}, err
}
ps := []Project{}
for rows.Next() {
p := Project{}
err := rows.Scan(&p.ID, &p.Title)
if err != nil {
db.assert.Nil(tx.Rollback())
return ps, err
}
ps = append(ps, p)
}
err = tx.Commit()
if err != nil {
return ps, err
}
return ps, nil
}