-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsource_filesystem_adapter.go
116 lines (96 loc) · 2.92 KB
/
source_filesystem_adapter.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
package adapt
import (
"fmt"
"io"
"log/slog"
"path"
"strings"
)
// DirEntry is similar to the fs.DirEntry interface provided by Go. It's used
// by adapt to allow implementations like NewMemoryFSSource to provide a
// minimal directory entry object.
type DirEntry interface {
IsDir() bool
Name() string
}
// FilesystemAdapter is a minimal interface for any filesystem. It can be used
// in combination with FromFilesystemAdapter to convert the FilesystemAdapter
// to a full SqlStatementsSource
type FilesystemAdapter interface {
ReadDir(name string) ([]DirEntry, error)
Open(name string) (io.ReadCloser, error)
}
type fsAdapter struct {
log *slog.Logger
adapter FilesystemAdapter
directory string
fsMap map[string]string
fsList []string
}
func (src *fsAdapter) Init(log *slog.Logger) error {
src.log = log
entries, err := src.adapter.ReadDir(src.directory)
if err != nil {
log.Error("unable to read directory content", "directory", src.directory, "error", err)
return err
}
filterMap := make(map[string]struct{})
for _, e := range entries {
if e.IsDir() {
continue
}
id := e.Name()
id = strings.TrimSuffix(id, ".sql")
if strings.HasSuffix(id, ".up") {
filterMap[strings.TrimSuffix(id, ".up")] = struct{}{}
} else if strings.HasSuffix(id, ".down") {
filterMap[strings.TrimSuffix(id, ".down")] = struct{}{}
} else {
log.Error("migration with invalid id. Doesn't have '.up.sql' or '.down.sql' suffix",
"migration_id", id, "filename", e.Name())
return fmt.Errorf("adapt.fsAdapter: migration with invalid id")
}
src.fsMap[id] = path.Join(src.directory, e.Name())
}
// generate list of map keys
for key := range filterMap {
src.fsList = append(src.fsList, key)
}
return nil
}
func (src *fsAdapter) ListMigrations() ([]string, error) {
return src.fsList, nil
}
func (src *fsAdapter) get(id, filename string) (*ParsedMigration, error) {
f, err := src.adapter.Open(filename)
if err != nil {
src.log.Error("unable to open file", "id", id, "filename", filename, "error", err)
return nil, err
}
defer func() {
_ = f.Close()
}()
return Parse(f)
}
func (src *fsAdapter) GetParsedUpMigration(id string) (*ParsedMigration, error) {
if filename, ok := src.fsMap[id+".up"]; ok {
return src.get(id, filename)
}
return nil, fmt.Errorf("adapt.fsAdapter: unable to find up migration for id %q", id)
}
func (src *fsAdapter) GetParsedDownMigration(id string) (*ParsedMigration, error) {
if filename, ok := src.fsMap[id+".down"]; ok {
return src.get(id, filename)
}
return nil, nil
}
// FromFilesystemAdapter converts an FilesystemAdapter implementation to a
// full-fledged SqlStatementsSource. It unifies the code across most filesystem
// and the in-memory statements sources.
func FromFilesystemAdapter(adapter FilesystemAdapter, directory string) SqlStatementsSource {
return &fsAdapter{
adapter: adapter,
directory: directory,
fsMap: make(map[string]string),
}
}