github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/blueprint/pathtools/fs.go (about) 1 // Copyright 2016 Google Inc. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package pathtools 16 17 import ( 18 "bytes" 19 "errors" 20 "fmt" 21 "io" 22 "io/ioutil" 23 "os" 24 "path/filepath" 25 "sort" 26 "strings" 27 ) 28 29 // Based on Andrew Gerrand's "10 things you (probably) dont' know about Go" 30 31 var OsFs FileSystem = osFs{} 32 33 func MockFs(files map[string][]byte) FileSystem { 34 fs := &mockFs{ 35 files: make(map[string][]byte, len(files)), 36 dirs: make(map[string]bool), 37 all: []string(nil), 38 } 39 40 for f, b := range files { 41 fs.files[filepath.Clean(f)] = b 42 dir := filepath.Dir(f) 43 for dir != "." && dir != "/" { 44 fs.dirs[dir] = true 45 dir = filepath.Dir(dir) 46 } 47 fs.dirs[dir] = true 48 } 49 50 for f := range fs.files { 51 fs.all = append(fs.all, f) 52 } 53 54 for d := range fs.dirs { 55 fs.all = append(fs.all, d) 56 } 57 58 sort.Strings(fs.all) 59 60 return fs 61 } 62 63 type FileSystem interface { 64 Open(name string) (io.ReadCloser, error) 65 Exists(name string) (bool, bool, error) 66 Glob(pattern string, excludes []string) (matches, dirs []string, err error) 67 glob(pattern string) (matches []string, err error) 68 IsDir(name string) (bool, error) 69 Lstat(name string) (os.FileInfo, error) 70 ListDirsRecursive(name string) (dirs []string, err error) 71 } 72 73 // osFs implements FileSystem using the local disk. 74 type osFs struct{} 75 76 func (osFs) Open(name string) (io.ReadCloser, error) { return os.Open(name) } 77 func (osFs) Exists(name string) (bool, bool, error) { 78 stat, err := os.Stat(name) 79 if err == nil { 80 return true, stat.IsDir(), nil 81 } else if os.IsNotExist(err) { 82 return false, false, nil 83 } else { 84 return false, false, err 85 } 86 } 87 88 func (osFs) IsDir(name string) (bool, error) { 89 info, err := os.Stat(name) 90 if err != nil { 91 return false, fmt.Errorf("unexpected error after glob: %s", err) 92 } 93 return info.IsDir(), nil 94 } 95 96 func (fs osFs) Glob(pattern string, excludes []string) (matches, dirs []string, err error) { 97 return startGlob(fs, pattern, excludes) 98 } 99 100 func (osFs) glob(pattern string) ([]string, error) { 101 return filepath.Glob(pattern) 102 } 103 104 func (osFs) Lstat(path string) (stats os.FileInfo, err error) { 105 return os.Lstat(path) 106 } 107 108 // Returns a list of all directories under dir 109 func (osFs) ListDirsRecursive(name string) (dirs []string, err error) { 110 err = filepath.Walk(name, func(path string, info os.FileInfo, err error) error { 111 if err != nil { 112 return err 113 } 114 115 if info.Mode().IsDir() { 116 name := info.Name() 117 if name[0] == '.' && name != "." { 118 return filepath.SkipDir 119 } 120 121 dirs = append(dirs, path) 122 } 123 return nil 124 }) 125 126 return dirs, err 127 } 128 129 type mockFs struct { 130 files map[string][]byte 131 dirs map[string]bool 132 all []string 133 } 134 135 func (m *mockFs) Open(name string) (io.ReadCloser, error) { 136 if f, ok := m.files[name]; ok { 137 return struct { 138 io.Closer 139 *bytes.Reader 140 }{ 141 ioutil.NopCloser(nil), 142 bytes.NewReader(f), 143 }, nil 144 } 145 146 return nil, &os.PathError{ 147 Op: "open", 148 Path: name, 149 Err: os.ErrNotExist, 150 } 151 } 152 153 func (m *mockFs) Exists(name string) (bool, bool, error) { 154 name = filepath.Clean(name) 155 if _, ok := m.files[name]; ok { 156 return ok, false, nil 157 } 158 if _, ok := m.dirs[name]; ok { 159 return ok, true, nil 160 } 161 return false, false, nil 162 } 163 164 func (m *mockFs) IsDir(name string) (bool, error) { 165 return m.dirs[filepath.Clean(name)], nil 166 } 167 168 func (m *mockFs) Glob(pattern string, excludes []string) (matches, dirs []string, err error) { 169 return startGlob(m, pattern, excludes) 170 } 171 172 func (m *mockFs) glob(pattern string) ([]string, error) { 173 var matches []string 174 for _, f := range m.all { 175 match, err := filepath.Match(pattern, f) 176 if err != nil { 177 return nil, err 178 } 179 if f == "." && f != pattern { 180 // filepath.Glob won't return "." unless the pattern was "." 181 match = false 182 } 183 if match { 184 matches = append(matches, f) 185 } 186 } 187 return matches, nil 188 } 189 190 func (m *mockFs) Lstat(path string) (stats os.FileInfo, err error) { 191 return nil, errors.New("Lstat is not yet implemented in MockFs") 192 } 193 194 func (m *mockFs) ListDirsRecursive(name string) (dirs []string, err error) { 195 name = filepath.Clean(name) 196 dirs = append(dirs, name) 197 if name == "." { 198 name = "" 199 } else if name != "/" { 200 name = name + "/" 201 } 202 for _, f := range m.all { 203 if _, isDir := m.dirs[f]; isDir && filepath.Base(f)[0] != '.' { 204 if strings.HasPrefix(f, name) && 205 strings.HasPrefix(f, "/") == strings.HasPrefix(name, "/") { 206 dirs = append(dirs, f) 207 } 208 } 209 } 210 211 return dirs, nil 212 }