filesystem.go (3781B)
1 // Copyright 2019 The CC Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package cc 6 7 import ( 8 "io" 9 "io/ioutil" 10 "os" 11 "path" 12 "strings" 13 "time" 14 ) 15 16 // Filesystem abstraction used in CC. The underlying value must be comparable (e.g. pointer) to be used in map keys. 17 type Filesystem interface { 18 // Stat is an analog of os.Stat, but also accepts a flag to indicate a system include (<file.h>). 19 Stat(path string, sys bool) (os.FileInfo, error) 20 // Open is an analog of os.Open, but also accepts a flag to indicate a system include (<file.h>). 21 Open(path string, sys bool) (io.ReadCloser, error) 22 } 23 24 // LocalFS returns a local filesystem implementation. 25 func LocalFS() Filesystem { 26 return localFS{} 27 } 28 29 type localFS struct{} 30 31 // Stat implements Filesystem. 32 func (localFS) Stat(path string, sys bool) (os.FileInfo, error) { 33 return os.Stat(path) 34 } 35 36 // Open implements Filesystem. 37 func (localFS) Open(path string, sys bool) (io.ReadCloser, error) { 38 return os.Open(path) 39 } 40 41 // WorkingDir is a filesystem implementation that resolves paths relative to a given directory. 42 // If filesystem is not specified, the local one will be used. 43 func WorkingDir(wd string, fs Filesystem) Filesystem { 44 if fs == nil { 45 fs = LocalFS() 46 } 47 return workDir{fs: fs, wd: wd} 48 } 49 50 type workDir struct { 51 fs Filesystem 52 wd string 53 } 54 55 // Stat implements Filesystem. 56 func (fs workDir) Stat(fname string, sys bool) (os.FileInfo, error) { 57 if !path.IsAbs(fname) { 58 fname = path.Join(fs.wd, fname) 59 } 60 return fs.fs.Stat(fname, sys) 61 } 62 63 // Open implements Filesystem. 64 func (fs workDir) Open(fname string, sys bool) (io.ReadCloser, error) { 65 if !path.IsAbs(fname) { 66 fname = path.Join(fs.wd, fname) 67 } 68 return fs.fs.Open(fname, sys) 69 } 70 71 // Overlay is a filesystem implementation that first check if the file is available in the primary FS 72 // and if not, falls back to a secondary FS. 73 func Overlay(pri, sec Filesystem) Filesystem { 74 return overlayFS{pri: pri, sec: sec} 75 } 76 77 type overlayFS struct { 78 pri, sec Filesystem 79 } 80 81 // Stat implements Filesystem. 82 func (fs overlayFS) Stat(path string, sys bool) (os.FileInfo, error) { 83 st, err := fs.pri.Stat(path, sys) 84 if err == nil || !os.IsNotExist(err) { 85 return st, err 86 } 87 return fs.sec.Stat(path, sys) 88 } 89 90 // Open implements Filesystem. 91 func (fs overlayFS) Open(path string, sys bool) (io.ReadCloser, error) { 92 f, err := fs.pri.Open(path, sys) 93 if err == nil || !os.IsNotExist(err) { 94 return f, err 95 } 96 return fs.sec.Open(path, sys) 97 } 98 99 // StaticFS implements filesystem interface by serving string values form the provided map. 100 func StaticFS(files map[string]string) Filesystem { 101 return &staticFS{m: files, ts: time.Now()} 102 } 103 104 type staticFS struct { 105 ts time.Time 106 m map[string]string 107 } 108 109 // Stat implements Filesystem. 110 func (fs *staticFS) Stat(path string, sys bool) (os.FileInfo, error) { 111 v, ok := fs.m[path] 112 if !ok { 113 return nil, &os.PathError{"stat", path, os.ErrNotExist} 114 } 115 return staticFileInfo{name: path, size: int64(len(v)), mode: 0, mod: fs.ts}, nil 116 } 117 118 // Open implements Filesystem. 119 func (fs *staticFS) Open(path string, sys bool) (io.ReadCloser, error) { 120 v, ok := fs.m[path] 121 if !ok { 122 return nil, &os.PathError{"open", path, os.ErrNotExist} 123 } 124 return ioutil.NopCloser(strings.NewReader(v)), nil 125 } 126 127 type staticFileInfo struct { 128 name string 129 size int64 130 mode os.FileMode 131 mod time.Time 132 } 133 134 func (fi staticFileInfo) Name() string { 135 return fi.name 136 } 137 138 func (fi staticFileInfo) Size() int64 { 139 return fi.size 140 } 141 142 func (fi staticFileInfo) Mode() os.FileMode { 143 return fi.mode 144 } 145 146 func (fi staticFileInfo) ModTime() time.Time { 147 return fi.mod 148 } 149 150 func (fi staticFileInfo) IsDir() bool { 151 return fi.mode.IsDir() 152 } 153 154 func (fi staticFileInfo) Sys() interface{} { 155 return fi 156 }