github.com/kruftik/go-migrate@v3.5.4+incompatible/source/godoc_vfs/vfs.go (about) 1 // Package vfs contains a driver that reads migrations from a virtual file 2 // system. 3 // 4 // Implementations of the filesystem interface that read from zip files and 5 // maps, as well as the definition of the filesystem interface can be found in 6 // the golang.org/x/tools/godoc/vfs package. 7 package godoc_vfs 8 9 import ( 10 "bytes" 11 "fmt" 12 "io" 13 "io/ioutil" 14 "os" 15 "path" 16 17 "github.com/golang-migrate/migrate/source" 18 "golang.org/x/tools/godoc/vfs" 19 ) 20 21 func init() { 22 source.Register("godoc-vfs", &VFS{}) 23 } 24 25 // VFS is an implementation of driver that returns migrations from a virtual 26 // file system. 27 type VFS struct { 28 migrations *source.Migrations 29 fs vfs.FileSystem 30 path string 31 } 32 33 // Open implements the source.Driver interface for VFS. 34 // 35 // Calling this function panics, instead use the WithInstance function. 36 // See the package level documentation for an example. 37 func (b *VFS) Open(url string) (source.Driver, error) { 38 panic("not implemented") 39 } 40 41 // WithInstance creates a new driver from a virtual file system. 42 // If a tree named searchPath exists in the virtual filesystem, WithInstance 43 // searches for migration files there. 44 // It defaults to "/". 45 func WithInstance(fs vfs.FileSystem, searchPath string) (source.Driver, error) { 46 if searchPath == "" { 47 searchPath = "/" 48 } 49 50 bn := &VFS{ 51 fs: fs, 52 path: searchPath, 53 migrations: source.NewMigrations(), 54 } 55 56 files, err := fs.ReadDir(searchPath) 57 if err != nil { 58 return nil, err 59 } 60 61 for _, fi := range files { 62 m, err := source.DefaultParse(fi.Name()) 63 if err != nil { 64 continue // ignore files that we can't parse 65 } 66 67 if !bn.migrations.Append(m) { 68 return nil, fmt.Errorf("unable to parse file %v", fi) 69 } 70 } 71 72 return bn, nil 73 } 74 75 // Close implements the source.Driver interface for VFS. 76 // It is a no-op and should not be used. 77 func (b *VFS) Close() error { 78 return nil 79 } 80 81 // First returns the first migration verion found in the file system. 82 // If no version is available os.ErrNotExist is returned. 83 func (b *VFS) First() (version uint, err error) { 84 v, ok := b.migrations.First() 85 if !ok { 86 return 0, &os.PathError{"first", "<vfs>://" + b.path, os.ErrNotExist} 87 } 88 return v, nil 89 } 90 91 // Prev returns the previous version available to the driver. 92 // If no previous version is available os.ErrNotExist is returned. 93 func (b *VFS) Prev(version uint) (prevVersion uint, err error) { 94 v, ok := b.migrations.Prev(version) 95 if !ok { 96 return 0, &os.PathError{fmt.Sprintf("prev for version %v", version), "<vfs>://" + b.path, os.ErrNotExist} 97 } 98 return v, nil 99 } 100 101 // Prev returns the next version available to the driver. 102 // If no previous version is available os.ErrNotExist is returned. 103 func (b *VFS) Next(version uint) (nextVersion uint, err error) { 104 v, ok := b.migrations.Next(version) 105 if !ok { 106 return 0, &os.PathError{fmt.Sprintf("next for version %v", version), "<vfs>://" + b.path, os.ErrNotExist} 107 } 108 return v, nil 109 } 110 111 // ReadUp returns the up migration body and an identifier that helps with 112 // finding this migration in the source. 113 // If there is no up migration available for this version it returns 114 // os.ErrNotExist. 115 func (b *VFS) ReadUp(version uint) (r io.ReadCloser, identifier string, err error) { 116 if m, ok := b.migrations.Up(version); ok { 117 body, err := vfs.ReadFile(b.fs, path.Join(b.path, m.Raw)) 118 if err != nil { 119 return nil, "", err 120 } 121 return ioutil.NopCloser(bytes.NewReader(body)), m.Identifier, nil 122 } 123 return nil, "", &os.PathError{fmt.Sprintf("read version %v", version), "<vfs>://" + b.path, os.ErrNotExist} 124 } 125 126 // ReadDown returns the down migration body and an identifier that helps with 127 // finding this migration in the source. 128 func (b *VFS) ReadDown(version uint) (r io.ReadCloser, identifier string, err error) { 129 if m, ok := b.migrations.Down(version); ok { 130 body, err := vfs.ReadFile(b.fs, path.Join(b.path, m.Raw)) 131 if err != nil { 132 return nil, "", err 133 } 134 return ioutil.NopCloser(bytes.NewReader(body)), m.Identifier, nil 135 } 136 return nil, "", &os.PathError{fmt.Sprintf("read version %v", version), "<vfs>://" + b.path, os.ErrNotExist} 137 }