github.com/dannyzhou2015/migrate/v4@v4.15.2/source/httpfs/partial_driver.go (about) 1 package httpfs 2 3 import ( 4 "errors" 5 "io" 6 "net/http" 7 "os" 8 "path" 9 "strconv" 10 11 "github.com/dannyzhou2015/migrate/v4/source" 12 ) 13 14 // PartialDriver is a helper service for creating new source drivers working with 15 // http.FileSystem instances. It implements all source.Driver interface methods 16 // except for Open(). New driver could embed this struct and add missing Open() 17 // method. 18 // 19 // To prepare PartialDriver for use Init() function. 20 type PartialDriver struct { 21 migrations *source.Migrations 22 fs http.FileSystem 23 path string 24 } 25 26 // Init prepares not initialized PartialDriver instance to read migrations from a 27 // http.FileSystem instance and a relative path. 28 func (p *PartialDriver) Init(fs http.FileSystem, path string) error { 29 root, err := fs.Open(path) 30 if err != nil { 31 return err 32 } 33 34 files, err := root.Readdir(0) 35 if err != nil { 36 _ = root.Close() 37 return err 38 } 39 if err = root.Close(); err != nil { 40 return err 41 } 42 43 ms := source.NewMigrations() 44 for _, file := range files { 45 if file.IsDir() { 46 continue 47 } 48 49 m, err := source.DefaultParse(file.Name()) 50 if err != nil { 51 continue // ignore files that we can't parse 52 } 53 54 if !ms.Append(m) { 55 return source.ErrDuplicateMigration{ 56 Migration: *m, 57 FileInfo: file, 58 } 59 } 60 } 61 62 p.fs = fs 63 p.path = path 64 p.migrations = ms 65 return nil 66 } 67 68 // Close is part of source.Driver interface implementation. This is a no-op. 69 func (p *PartialDriver) Close() error { 70 return nil 71 } 72 73 // First is part of source.Driver interface implementation. 74 func (p *PartialDriver) First() (version uint, err error) { 75 if version, ok := p.migrations.First(); ok { 76 return version, nil 77 } 78 return 0, &os.PathError{ 79 Op: "first", 80 Path: p.path, 81 Err: os.ErrNotExist, 82 } 83 } 84 85 // Prev is part of source.Driver interface implementation. 86 func (p *PartialDriver) Prev(version uint) (prevVersion uint, err error) { 87 if version, ok := p.migrations.Prev(version); ok { 88 return version, nil 89 } 90 return 0, &os.PathError{ 91 Op: "prev for version " + strconv.FormatUint(uint64(version), 10), 92 Path: p.path, 93 Err: os.ErrNotExist, 94 } 95 } 96 97 // Next is part of source.Driver interface implementation. 98 func (p *PartialDriver) Next(version uint) (nextVersion uint, err error) { 99 if version, ok := p.migrations.Next(version); ok { 100 return version, nil 101 } 102 return 0, &os.PathError{ 103 Op: "next for version " + strconv.FormatUint(uint64(version), 10), 104 Path: p.path, 105 Err: os.ErrNotExist, 106 } 107 } 108 109 // ReadUp is part of source.Driver interface implementation. 110 func (p *PartialDriver) ReadUp(version uint) (r io.ReadCloser, identifier string, err error) { 111 if m, ok := p.migrations.Up(version); ok { 112 body, err := p.open(path.Join(p.path, m.Raw)) 113 if err != nil { 114 return nil, "", err 115 } 116 return body, m.Identifier, nil 117 } 118 return nil, "", &os.PathError{ 119 Op: "read up for version " + strconv.FormatUint(uint64(version), 10), 120 Path: p.path, 121 Err: os.ErrNotExist, 122 } 123 } 124 125 // ReadDown is part of source.Driver interface implementation. 126 func (p *PartialDriver) ReadDown(version uint) (r io.ReadCloser, identifier string, err error) { 127 if m, ok := p.migrations.Down(version); ok { 128 body, err := p.open(path.Join(p.path, m.Raw)) 129 if err != nil { 130 return nil, "", err 131 } 132 return body, m.Identifier, nil 133 } 134 return nil, "", &os.PathError{ 135 Op: "read down for version " + strconv.FormatUint(uint64(version), 10), 136 Path: p.path, 137 Err: os.ErrNotExist, 138 } 139 } 140 141 func (p *PartialDriver) open(path string) (http.File, error) { 142 f, err := p.fs.Open(path) 143 if err == nil { 144 return f, nil 145 } 146 // Some non-standard file systems may return errors that don't include the path, that 147 // makes debugging harder. 148 if !errors.As(err, new(*os.PathError)) { 149 err = &os.PathError{ 150 Op: "open", 151 Path: path, 152 Err: err, 153 } 154 } 155 return nil, err 156 }