github.com/dannyzhou2015/migrate/v4@v4.15.2/source/iofs/iofs.go (about) 1 //go:build go1.16 2 // +build go1.16 3 4 package iofs 5 6 import ( 7 "errors" 8 "fmt" 9 "io" 10 "io/fs" 11 "path" 12 "strconv" 13 14 "github.com/dannyzhou2015/migrate/v4/source" 15 ) 16 17 type driver struct { 18 PartialDriver 19 } 20 21 // New returns a new Driver from io/fs#FS and a relative path. 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 // Open is part of source.Driver interface implementation. 31 // Open cannot be called on the iofs passthrough driver. 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 // PartialDriver is a helper service for creating new source drivers working with 37 // io/fs.FS instances. It implements all source.Driver interface methods 38 // except for Open(). New driver could embed this struct and add missing Open() 39 // method. 40 // 41 // To prepare PartialDriver for use Init() function. 42 type PartialDriver struct { 43 migrations *source.Migrations 44 fsys fs.FS 45 path string 46 } 47 48 // Init prepares not initialized IoFS instance to read migrations from a 49 // io/fs#FS instance and a relative path. 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 // Close is part of source.Driver interface implementation. 84 // Closes the file system if possible. 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 // First is part of source.Driver interface implementation. 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 // Prev is part of source.Driver interface implementation. 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 // Next is part of source.Driver interface implementation. 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 // ReadUp is part of source.Driver interface implementation. 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 // ReadDown is part of source.Driver interface implementation. 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 // Some non-standard file systems may return errors that don't include the path, that 167 // makes debugging harder. 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 }