-
Notifications
You must be signed in to change notification settings - Fork 3
/
load.go
158 lines (130 loc) · 3.25 KB
/
load.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package dbump
import (
"fmt"
"io/fs"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
)
type FS interface {
fs.ReadDirFS
fs.ReadFileFS
}
// DiskLoader can load migrations from disk/OS.
type DiskLoader struct {
path string
}
// NewDiskLoader instantiates a new DiskLoader.
func NewDiskLoader(path string) *DiskLoader {
return &DiskLoader{
path: strings.TrimRight(path, string(os.PathSeparator)),
}
}
// Load is a method for Loader interface.
func (dl *DiskLoader) Load() ([]*Migration, error) {
return loadMigrationsFromFS(osFS{}, dl.path)
}
// FileSysLoader can load migrations from fs.FS.
type FileSysLoader struct {
fsys FS
path string
}
// NewFileSysLoader instantiates a new FileSysLoader.
func NewFileSysLoader(fsys FS, path string) *FileSysLoader {
return &FileSysLoader{
fsys: fsys,
path: strings.TrimRight(path, string(os.PathSeparator)),
}
}
// Load is a method for Loader interface.
func (el *FileSysLoader) Load() ([]*Migration, error) {
return loadMigrationsFromFS(el.fsys, el.path)
}
// SliceLoader loads given migrations.
type SliceLoader struct {
migrations []*Migration
}
// NewSliceLoader instantiates a new SliceLoader.
func NewSliceLoader(migrations []*Migration) *SliceLoader {
return &SliceLoader{
migrations: migrations,
}
}
// Load is a method for Loader interface.
func (sl *SliceLoader) Load() ([]*Migration, error) {
return sl.migrations, nil
}
// AddMigration to loader.
func (sl *SliceLoader) AddMigration(m *Migration) {
if m == nil {
panic("dbump: migration should not be nil")
}
sl.migrations = append(sl.migrations, m)
}
var migrationRE = regexp.MustCompile(`^(\d+)_.+\.sql$`)
func loadMigrationsFromFS(fsys FS, path string) ([]*Migration, error) {
files, err := fsys.ReadDir(path)
if err != nil {
return nil, err
}
migs := make([]*Migration, 0, len(files))
for _, fi := range files {
if fi.IsDir() {
continue
}
matches := migrationRE.FindStringSubmatch(fi.Name())
if len(matches) != 2 {
continue
}
m, err := loadMigrationFromFS(fsys, path, matches[1], fi.Name())
if err != nil {
return nil, err
}
migs = append(migs, m)
}
return migs, nil
}
func loadMigrationFromFS(fsys FS, path, id, name string) (*Migration, error) {
n, err := strconv.ParseInt(id, 10, 32)
if err != nil {
return nil, err
}
body, err := fsys.ReadFile(filepath.Join(path, name))
if err != nil {
return nil, err
}
m, err := parseMigration(body)
if err != nil {
return nil, err
}
m.ID = int(n)
m.Name = name
return m, nil
}
func parseMigration(body []byte) (*Migration, error) {
parts := strings.Split(string(body), MigrationDelimiter)
if size := len(parts); size != 2 {
return nil, fmt.Errorf("should have 2 parts separated by MigrationDelimiter but got: %d", size)
}
applySQL := strings.TrimSpace(parts[0])
revertSQL := strings.TrimSpace(parts[1])
return &Migration{
Apply: applySQL,
Revert: revertSQL,
}, nil
}
type osFS struct{}
// Open implements dbump.FS interface.
func (osFS) Open(name string) (fs.File, error) {
panic("unreachable")
}
// ReadDir implements dbump.FS interface.
func (osFS) ReadDir(name string) ([]os.DirEntry, error) {
return os.ReadDir(name)
}
// ReadFile implements dbump.FS interface.
func (osFS) ReadFile(name string) ([]byte, error) {
return os.ReadFile(name)
}