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 "fmt" 20 "io" 21 "io/ioutil" 22 "os" 23 "path/filepath" 24 ) 25 26 // Based on Andrew Gerrand's "10 things you (probably) dont' know about Go" 27 28 var OsFs FileSystem = osFs{} 29 30 func MockFs(files map[string][]byte) FileSystem { 31 fs := &mockFs{ 32 files: make(map[string][]byte, len(files)), 33 dirs: make(map[string]bool), 34 all: []string(nil), 35 } 36 37 for f, b := range files { 38 fs.files[filepath.Clean(f)] = b 39 dir := filepath.Dir(f) 40 for dir != "." && dir != "/" { 41 fs.dirs[dir] = true 42 dir = filepath.Dir(dir) 43 } 44 fs.dirs[dir] = true 45 } 46 47 for f := range fs.files { 48 fs.all = append(fs.all, f) 49 } 50 51 for d := range fs.dirs { 52 fs.all = append(fs.all, d) 53 } 54 55 return fs 56 } 57 58 type FileSystem interface { 59 Open(name string) (io.ReadCloser, error) 60 Exists(name string) (bool, bool, error) 61 Glob(pattern string, excludes []string) (matches, dirs []string, err error) 62 glob(pattern string) (matches []string, err error) 63 IsDir(name string) (bool, error) 64 } 65 66 // osFs implements FileSystem using the local disk. 67 type osFs struct{} 68 69 func (osFs) Open(name string) (io.ReadCloser, error) { return os.Open(name) } 70 func (osFs) Exists(name string) (bool, bool, error) { 71 stat, err := os.Stat(name) 72 if err == nil { 73 return true, stat.IsDir(), nil 74 } else if os.IsNotExist(err) { 75 return false, false, nil 76 } else { 77 return false, false, err 78 } 79 } 80 81 func (osFs) IsDir(name string) (bool, error) { 82 info, err := os.Stat(name) 83 if err != nil { 84 return false, fmt.Errorf("unexpected error after glob: %s", err) 85 } 86 return info.IsDir(), nil 87 } 88 89 func (fs osFs) Glob(pattern string, excludes []string) (matches, dirs []string, err error) { 90 return startGlob(fs, pattern, excludes) 91 } 92 93 func (osFs) glob(pattern string) ([]string, error) { 94 return filepath.Glob(pattern) 95 } 96 97 type mockFs struct { 98 files map[string][]byte 99 dirs map[string]bool 100 all []string 101 } 102 103 func (m *mockFs) Open(name string) (io.ReadCloser, error) { 104 if f, ok := m.files[name]; ok { 105 return struct { 106 io.Closer 107 *bytes.Reader 108 }{ 109 ioutil.NopCloser(nil), 110 bytes.NewReader(f), 111 }, nil 112 } 113 114 return nil, &os.PathError{ 115 Op: "open", 116 Path: name, 117 Err: os.ErrNotExist, 118 } 119 } 120 121 func (m *mockFs) Exists(name string) (bool, bool, error) { 122 name = filepath.Clean(name) 123 if _, ok := m.files[name]; ok { 124 return ok, false, nil 125 } 126 if _, ok := m.dirs[name]; ok { 127 return ok, true, nil 128 } 129 return false, false, nil 130 } 131 132 func (m *mockFs) IsDir(name string) (bool, error) { 133 return m.dirs[filepath.Clean(name)], nil 134 } 135 136 func (m *mockFs) Glob(pattern string, excludes []string) (matches, dirs []string, err error) { 137 return startGlob(m, pattern, excludes) 138 } 139 140 func (m *mockFs) glob(pattern string) ([]string, error) { 141 var matches []string 142 for _, f := range m.all { 143 match, err := filepath.Match(pattern, f) 144 if err != nil { 145 return nil, err 146 } 147 if match { 148 matches = append(matches, f) 149 } 150 } 151 return matches, nil 152 } 153