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  }