1
2
3
4 package iofs
5
6 import (
7 "errors"
8 "fmt"
9 "io"
10 "io/fs"
11 "path"
12 "strconv"
13
14 "github.com/golang-migrate/migrate/v4/source"
15 )
16
17 type driver struct {
18 PartialDriver
19 }
20
21
22 func New(fsys fs.FS, path string) (source.Driver, error) {
23 var i driver
24 if err := i.Init(fsys, path); err != nil {
25 return nil, fmt.Errorf("failed to init driver with path %s: %w", path, err)
26 }
27 return &i, nil
28 }
29
30
31
32 func (d *driver) Open(url string) (source.Driver, error) {
33 return nil, errors.New("Open() cannot be called on the iofs passthrough driver")
34 }
35
36
37
38
39
40
41
42 type PartialDriver struct {
43 migrations *source.Migrations
44 fsys fs.FS
45 path string
46 }
47
48
49
50 func (d *PartialDriver) Init(fsys fs.FS, path string) error {
51 entries, err := fs.ReadDir(fsys, path)
52 if err != nil {
53 return err
54 }
55
56 ms := source.NewMigrations()
57 for _, e := range entries {
58 if e.IsDir() {
59 continue
60 }
61 m, err := source.DefaultParse(e.Name())
62 if err != nil {
63 continue
64 }
65 file, err := e.Info()
66 if err != nil {
67 return err
68 }
69 if !ms.Append(m) {
70 return source.ErrDuplicateMigration{
71 Migration: *m,
72 FileInfo: file,
73 }
74 }
75 }
76
77 d.fsys = fsys
78 d.path = path
79 d.migrations = ms
80 return nil
81 }
82
83
84
85 func (d *PartialDriver) Close() error {
86 c, ok := d.fsys.(io.Closer)
87 if !ok {
88 return nil
89 }
90 return c.Close()
91 }
92
93
94 func (d *PartialDriver) First() (version uint, err error) {
95 if version, ok := d.migrations.First(); ok {
96 return version, nil
97 }
98 return 0, &fs.PathError{
99 Op: "first",
100 Path: d.path,
101 Err: fs.ErrNotExist,
102 }
103 }
104
105
106 func (d *PartialDriver) Prev(version uint) (prevVersion uint, err error) {
107 if version, ok := d.migrations.Prev(version); ok {
108 return version, nil
109 }
110 return 0, &fs.PathError{
111 Op: "prev for version " + strconv.FormatUint(uint64(version), 10),
112 Path: d.path,
113 Err: fs.ErrNotExist,
114 }
115 }
116
117
118 func (d *PartialDriver) Next(version uint) (nextVersion uint, err error) {
119 if version, ok := d.migrations.Next(version); ok {
120 return version, nil
121 }
122 return 0, &fs.PathError{
123 Op: "next for version " + strconv.FormatUint(uint64(version), 10),
124 Path: d.path,
125 Err: fs.ErrNotExist,
126 }
127 }
128
129
130 func (d *PartialDriver) ReadUp(version uint) (r io.ReadCloser, identifier string, err error) {
131 if m, ok := d.migrations.Up(version); ok {
132 body, err := d.open(path.Join(d.path, m.Raw))
133 if err != nil {
134 return nil, "", err
135 }
136 return body, m.Identifier, nil
137 }
138 return nil, "", &fs.PathError{
139 Op: "read up for version " + strconv.FormatUint(uint64(version), 10),
140 Path: d.path,
141 Err: fs.ErrNotExist,
142 }
143 }
144
145
146 func (d *PartialDriver) ReadDown(version uint) (r io.ReadCloser, identifier string, err error) {
147 if m, ok := d.migrations.Down(version); ok {
148 body, err := d.open(path.Join(d.path, m.Raw))
149 if err != nil {
150 return nil, "", err
151 }
152 return body, m.Identifier, nil
153 }
154 return nil, "", &fs.PathError{
155 Op: "read down for version " + strconv.FormatUint(uint64(version), 10),
156 Path: d.path,
157 Err: fs.ErrNotExist,
158 }
159 }
160
161 func (d *PartialDriver) open(path string) (fs.File, error) {
162 f, err := d.fsys.Open(path)
163 if err == nil {
164 return f, nil
165 }
166
167
168 if !errors.As(err, new(*fs.PathError)) {
169 err = &fs.PathError{
170 Op: "open",
171 Path: path,
172 Err: err,
173 }
174 }
175 return nil, err
176 }
177
View as plain text