github.com/maxnasonov/afero@v1.8.4/iofs.go (about) 1 // +build go1.16 2 3 package afero 4 5 import ( 6 "io" 7 "io/fs" 8 "os" 9 "path" 10 "time" 11 ) 12 13 // IOFS adopts afero.Fs to stdlib io/fs.FS 14 type IOFS struct { 15 Fs 16 } 17 18 func NewIOFS(fs Fs) IOFS { 19 return IOFS{Fs: fs} 20 } 21 22 var ( 23 _ fs.FS = IOFS{} 24 _ fs.GlobFS = IOFS{} 25 _ fs.ReadDirFS = IOFS{} 26 _ fs.ReadFileFS = IOFS{} 27 _ fs.StatFS = IOFS{} 28 _ fs.SubFS = IOFS{} 29 ) 30 31 func (iofs IOFS) Open(name string) (fs.File, error) { 32 const op = "open" 33 34 // by convention for fs.FS implementations we should perform this check 35 if !fs.ValidPath(name) { 36 return nil, iofs.wrapError(op, name, fs.ErrInvalid) 37 } 38 39 file, err := iofs.Fs.Open(name) 40 if err != nil { 41 return nil, iofs.wrapError(op, name, err) 42 } 43 44 // file should implement fs.ReadDirFile 45 if _, ok := file.(fs.ReadDirFile); !ok { 46 file = readDirFile{file} 47 } 48 49 return file, nil 50 } 51 52 func (iofs IOFS) Glob(pattern string) ([]string, error) { 53 const op = "glob" 54 55 // afero.Glob does not perform this check but it's required for implementations 56 if _, err := path.Match(pattern, ""); err != nil { 57 return nil, iofs.wrapError(op, pattern, err) 58 } 59 60 items, err := Glob(iofs.Fs, pattern) 61 if err != nil { 62 return nil, iofs.wrapError(op, pattern, err) 63 } 64 65 return items, nil 66 } 67 68 func (iofs IOFS) ReadDir(name string) ([]fs.DirEntry, error) { 69 items, err := ReadDir(iofs.Fs, name) 70 if err != nil { 71 return nil, iofs.wrapError("readdir", name, err) 72 } 73 74 ret := make([]fs.DirEntry, len(items)) 75 for i := range items { 76 ret[i] = dirEntry{items[i]} 77 } 78 79 return ret, nil 80 } 81 82 func (iofs IOFS) ReadFile(name string) ([]byte, error) { 83 const op = "readfile" 84 85 if !fs.ValidPath(name) { 86 return nil, iofs.wrapError(op, name, fs.ErrInvalid) 87 } 88 89 bytes, err := ReadFile(iofs.Fs, name) 90 if err != nil { 91 return nil, iofs.wrapError(op, name, err) 92 } 93 94 return bytes, nil 95 } 96 97 func (iofs IOFS) Sub(dir string) (fs.FS, error) { return IOFS{NewBasePathFs(iofs.Fs, dir)}, nil } 98 99 func (IOFS) wrapError(op, path string, err error) error { 100 if _, ok := err.(*fs.PathError); ok { 101 return err // don't need to wrap again 102 } 103 104 return &fs.PathError{ 105 Op: op, 106 Path: path, 107 Err: err, 108 } 109 } 110 111 // dirEntry provides adapter from os.FileInfo to fs.DirEntry 112 type dirEntry struct { 113 fs.FileInfo 114 } 115 116 var _ fs.DirEntry = dirEntry{} 117 118 func (d dirEntry) Type() fs.FileMode { return d.FileInfo.Mode().Type() } 119 120 func (d dirEntry) Info() (fs.FileInfo, error) { return d.FileInfo, nil } 121 122 // readDirFile provides adapter from afero.File to fs.ReadDirFile needed for correct Open 123 type readDirFile struct { 124 File 125 } 126 127 var _ fs.ReadDirFile = readDirFile{} 128 129 func (r readDirFile) ReadDir(n int) ([]fs.DirEntry, error) { 130 items, err := r.File.Readdir(n) 131 if err != nil { 132 return nil, err 133 } 134 135 ret := make([]fs.DirEntry, len(items)) 136 for i := range items { 137 ret[i] = dirEntry{items[i]} 138 } 139 140 return ret, nil 141 } 142 143 // FromIOFS adopts io/fs.FS to use it as afero.Fs 144 // Note that io/fs.FS is read-only so all mutating methods will return fs.PathError with fs.ErrPermission 145 // To store modifications you may use afero.CopyOnWriteFs 146 type FromIOFS struct { 147 fs.FS 148 } 149 150 var _ Fs = FromIOFS{} 151 152 func (f FromIOFS) Create(name string) (File, error) { return nil, notImplemented("create", name) } 153 154 func (f FromIOFS) Mkdir(name string, perm os.FileMode) error { return notImplemented("mkdir", name) } 155 156 func (f FromIOFS) MkdirAll(path string, perm os.FileMode) error { 157 return notImplemented("mkdirall", path) 158 } 159 160 func (f FromIOFS) Open(name string) (File, error) { 161 file, err := f.FS.Open(name) 162 if err != nil { 163 return nil, err 164 } 165 166 return fromIOFSFile{File: file, name: name}, nil 167 } 168 169 func (f FromIOFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) { 170 return f.Open(name) 171 } 172 173 func (f FromIOFS) Remove(name string) error { 174 return notImplemented("remove", name) 175 } 176 177 func (f FromIOFS) RemoveAll(path string) error { 178 return notImplemented("removeall", path) 179 } 180 181 func (f FromIOFS) Rename(oldname, newname string) error { 182 return notImplemented("rename", oldname) 183 } 184 185 func (f FromIOFS) Stat(name string) (os.FileInfo, error) { return fs.Stat(f.FS, name) } 186 187 func (f FromIOFS) Name() string { return "fromiofs" } 188 189 func (f FromIOFS) Chmod(name string, mode os.FileMode) error { 190 return notImplemented("chmod", name) 191 } 192 193 func (f FromIOFS) Chown(name string, uid, gid int) error { 194 return notImplemented("chown", name) 195 } 196 197 func (f FromIOFS) Chtimes(name string, atime time.Time, mtime time.Time) error { 198 return notImplemented("chtimes", name) 199 } 200 201 type fromIOFSFile struct { 202 fs.File 203 name string 204 } 205 206 func (f fromIOFSFile) ReadAt(p []byte, off int64) (n int, err error) { 207 readerAt, ok := f.File.(io.ReaderAt) 208 if !ok { 209 return -1, notImplemented("readat", f.name) 210 } 211 212 return readerAt.ReadAt(p, off) 213 } 214 215 func (f fromIOFSFile) Seek(offset int64, whence int) (int64, error) { 216 seeker, ok := f.File.(io.Seeker) 217 if !ok { 218 return -1, notImplemented("seek", f.name) 219 } 220 221 return seeker.Seek(offset, whence) 222 } 223 224 func (f fromIOFSFile) Write(p []byte) (n int, err error) { 225 return -1, notImplemented("write", f.name) 226 } 227 228 func (f fromIOFSFile) WriteAt(p []byte, off int64) (n int, err error) { 229 return -1, notImplemented("writeat", f.name) 230 } 231 232 func (f fromIOFSFile) Name() string { return f.name } 233 234 func (f fromIOFSFile) Readdir(count int) ([]os.FileInfo, error) { 235 rdfile, ok := f.File.(fs.ReadDirFile) 236 if !ok { 237 return nil, notImplemented("readdir", f.name) 238 } 239 240 entries, err := rdfile.ReadDir(count) 241 if err != nil { 242 return nil, err 243 } 244 245 ret := make([]os.FileInfo, len(entries)) 246 for i := range entries { 247 ret[i], err = entries[i].Info() 248 249 if err != nil { 250 return nil, err 251 } 252 } 253 254 return ret, nil 255 } 256 257 func (f fromIOFSFile) Readdirnames(n int) ([]string, error) { 258 rdfile, ok := f.File.(fs.ReadDirFile) 259 if !ok { 260 return nil, notImplemented("readdir", f.name) 261 } 262 263 entries, err := rdfile.ReadDir(n) 264 if err != nil { 265 return nil, err 266 } 267 268 ret := make([]string, len(entries)) 269 for i := range entries { 270 ret[i] = entries[i].Name() 271 } 272 273 return ret, nil 274 } 275 276 func (f fromIOFSFile) Sync() error { return nil } 277 278 func (f fromIOFSFile) Truncate(size int64) error { 279 return notImplemented("truncate", f.name) 280 } 281 282 func (f fromIOFSFile) WriteString(s string) (ret int, err error) { 283 return -1, notImplemented("writestring", f.name) 284 } 285 286 func notImplemented(op, path string) error { 287 return &fs.PathError{Op: op, Path: path, Err: fs.ErrPermission} 288 }