github.com/eatigo/migrate@v3.0.2-0.20210729130915-7610befb1b6b+incompatible/source/file/file.go (about) 1 package file 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 nurl "net/url" 8 "os" 9 "path" 10 "path/filepath" 11 12 "strings" 13 14 "github.com/eatigo/migrate/source" 15 ) 16 17 func init() { 18 source.Register("file", &File{}) 19 } 20 21 type File struct { 22 url string 23 pathMap map[string]string 24 path string 25 migrations *source.Migrations 26 } 27 28 type fileInfoExt struct { 29 os.FileInfo 30 path string 31 } 32 33 func (f *File) listFiles(dirs string) ([]fileInfoExt, error) { 34 urls := strings.Split(dirs, ",") 35 36 var files []fileInfoExt 37 for _, url := range urls { 38 u, err := nurl.Parse(url) 39 if err != nil { 40 return nil, err 41 } 42 43 // concat host and path to restore full path 44 // host might be `.` 45 p := u.Host + u.Path 46 47 if len(p) == 0 { 48 // default to current directory if no path 49 wd, err := os.Getwd() 50 if err != nil { 51 return nil, err 52 } 53 p = wd 54 55 } else if p[0:1] == "." || p[0:1] != "/" { 56 // make path absolute if relative 57 abs, err := filepath.Abs(p) 58 if err != nil { 59 return nil, err 60 } 61 p = abs 62 } 63 64 // scan directory 65 localFiles, err := ioutil.ReadDir(p) 66 if err != nil { 67 return nil, err 68 } 69 70 for _, fi := range localFiles { 71 files = append(files, fileInfoExt{FileInfo: fi, path: p}) 72 } 73 } 74 75 return files, nil 76 } 77 78 func (f *File) Open(url string) (source.Driver, error) { 79 80 files, err := f.listFiles(url) 81 if err != nil { 82 return nil, err 83 } 84 85 nf := &File{ 86 url: url, 87 migrations: source.NewMigrations(), 88 path: url, 89 pathMap: map[string]string{}, 90 } 91 92 for _, fi := range files { 93 if !fi.IsDir() { 94 m, err := source.DefaultParse(fi.Name()) 95 if err != nil { 96 continue // ignore files that we can't parse 97 } 98 if !nf.migrations.Append(m) { 99 return nil, fmt.Errorf("unable to parse file %v", fi.Name()) 100 } 101 nf.pathMap[fi.Name()] = fi.path 102 } 103 } 104 105 return nf, nil 106 } 107 108 func (f *File) Close() error { 109 // nothing do to here 110 return nil 111 } 112 113 func (f *File) First() (version uint, err error) { 114 if v, ok := f.migrations.First(); !ok { 115 return 0, &os.PathError{"first", f.path, os.ErrNotExist} 116 } else { 117 return v, nil 118 } 119 } 120 121 func (f *File) Prev(version uint) (prevVersion uint, err error) { 122 if v, ok := f.migrations.Prev(version); !ok { 123 return 0, &os.PathError{fmt.Sprintf("prev for version %v", version), f.path, os.ErrNotExist} 124 } else { 125 return v, nil 126 } 127 } 128 129 func (f *File) Next(version uint) (nextVersion uint, err error) { 130 if v, ok := f.migrations.Next(version); !ok { 131 return 0, &os.PathError{fmt.Sprintf("next for version %v", version), f.path, os.ErrNotExist} 132 } else { 133 return v, nil 134 } 135 } 136 137 func (f *File) ReadUp(version uint) (r io.ReadCloser, identifier string, err error) { 138 if m, ok := f.migrations.Up(version); ok { 139 r, err := os.Open(path.Join(f.pathMap[m.Raw], m.Raw)) 140 if err != nil { 141 return nil, "", err 142 } 143 return r, m.Identifier, nil 144 } 145 return nil, "", &os.PathError{fmt.Sprintf("read version %v", version), f.path, os.ErrNotExist} 146 } 147 148 func (f *File) ReadDown(version uint) (r io.ReadCloser, identifier string, err error) { 149 if m, ok := f.migrations.Down(version); ok { 150 r, err := os.Open(path.Join(f.pathMap[m.Raw], m.Raw)) 151 if err != nil { 152 return nil, "", err 153 } 154 return r, m.Identifier, nil 155 } 156 return nil, "", &os.PathError{fmt.Sprintf("read version %v", version), f.path, os.ErrNotExist} 157 }