gitlab.com/tcpack/afero@v1.8.1-0.20220115110031-6a6be7a08c5d/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  }