From 71cd17bb97718d99f0d3caad916efae53796fd76 Mon Sep 17 00:00:00 2001 From: "Gustavo \"Guz\" L de Mello" Date: Wed, 12 Mar 2025 10:15:38 -0300 Subject: [PATCH] feat(database): make all database operation be methods instead of methods+structs This is inspired by the output code generated by sqlc --- database/database.go | 35 ++++-------- database/projects.go | 124 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 116 insertions(+), 43 deletions(-) diff --git a/database/database.go b/database/database.go index 5396030..453f3c5 100644 --- a/database/database.go +++ b/database/database.go @@ -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) -} diff --git a/database/projects.go b/database/projects.go index 33777fe..a243eb4 100644 --- a/database/projects.go +++ b/database/projects.go @@ -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 }