generated from ZEISS/template-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
builder.go
132 lines (105 loc) · 3.1 KB
/
builder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package seed
import (
"context"
"database/sql"
"errors"
"io"
"gorm.io/gorm"
)
// Migrator is a method that runs the migration.
type Migrator interface {
// Migrate is a method that runs the migration.
Migrate(context.Context, ...any) error
}
// Database provides methods for transactional operations.
type Database[R, W any] interface {
// ReadTx starts a read only transaction.
ReadTx(context.Context, func(context.Context, R) error) error
// ReadWriteTx starts a read write transaction.
ReadWriteTx(context.Context, func(context.Context, W) error) error
Migrator
io.Closer
}
// QueryError is an error that occurred while executing a query.
type QueryError struct {
// Query is the query that caused the error.
Query string
// Err is the error that occurred.
Err error
}
// Error implements the error interface.
func (e *QueryError) Error() string { return e.Query + ": " + e.Err.Error() }
// Unwrap implements the errors.Wrapper interface.
func (e *QueryError) Unwrap() error { return e.Err }
// NewQueryError returns a new QueryError.
func NewQueryError(query string, err error) *QueryError {
return &QueryError{
Query: query,
Err: err,
}
}
type databaseImpl[R, W any] struct {
r ReadTxFactory[R]
rw ReadWriteTxFactory[W]
conn *gorm.DB
}
// ReadTxFactory is a function that creates a new instance of Datastore.
type ReadTxFactory[R any] func(*gorm.DB) (R, error)
// ReadWriteTxFactory is a function that creates a new instance of Datastore.
type ReadWriteTxFactory[W any] func(*gorm.DB) (W, error)
// NewDatabase returns a new instance of db.
func NewDatabase[R, W any](conn *gorm.DB, r ReadTxFactory[R], rw ReadWriteTxFactory[W]) (Database[R, W], error) {
return &databaseImpl[R, W]{r, rw, conn}, nil
}
// Close closes the database connection.
func (d *databaseImpl[R, W]) Close() error {
db, err := d.conn.DB()
if err != nil {
return err
}
return db.Close()
}
// RunMigrations runs the database migrations.
func (d *databaseImpl[R, W]) Migrate(ctx context.Context, dst ...interface{}) error {
return d.conn.WithContext(ctx).AutoMigrate(dst...)
}
// ReadWriteTx starts a read only transaction.
func (d *databaseImpl[R, W]) ReadWriteTx(ctx context.Context, fn func(context.Context, W) error) error {
tx := d.conn.WithContext(ctx).Begin()
if tx.Error != nil {
return tx.Error
}
rwtx, err := d.rw(tx)
if err != nil {
return err
}
if err := fn(ctx, rwtx); err != nil {
tx.Rollback()
}
if err := tx.Commit().Error; err != nil && !errors.Is(err, sql.ErrTxDone) {
return err
}
return nil
}
// ReadTx starts a read only transaction.
func (d *databaseImpl[R, W]) ReadTx(ctx context.Context, fn func(context.Context, R) error) error {
tx := d.conn.WithContext(ctx).Begin()
if tx.Error != nil {
return NewQueryError("begin read transaction", tx.Error)
}
rtx, err := d.r(tx)
if err != nil {
return err
}
err = fn(ctx, rtx)
if err != nil {
tx.Rollback()
}
if err := tx.Commit().Error; err != nil && !errors.Is(err, sql.ErrTxDone) {
return NewQueryError("commit read transaction", err)
}
if err != nil {
return NewQueryError("commit read transaction", err)
}
return nil
}