package vfs import ( "os" "time" "github.com/spf13/afero" ) var _ afero.Fs = &FallbackFs{} // FallbackFs presents a source Fs that falls back to a backup Fs when the Fs call fails. type FallbackFs struct { source afero.Fs backup afero.Fs } // NewFallbackFs creates a new FallbackFs func NewFallbackFs(source, backup afero.Fs) *FallbackFs { return &FallbackFs{source, backup} } // NewOsFsFallback creates a new Fs based on the os package, restricts access on the supplied path and uses backup as fallback. func NewOsFsFallback(path string, backup afero.Fs) *FallbackFs { osFs := afero.NewOsFs() return NewFallbackFs(afero.NewBasePathFs(osFs, path), backup) } func (f *FallbackFs) do(action func(afero.Fs) error) error { err := action(f.source) if err == nil { return nil } return action(f.backup) } func (f *FallbackFs) doFile(action func(afero.Fs) (afero.File, error)) (afero.File, error) { file, err := action(f.source) if err == nil { return file.(afero.File), nil } return action(f.backup) } func (f *FallbackFs) doFileInfo(action func(afero.Fs) (os.FileInfo, error)) (os.FileInfo, error) { res, err := action(f.source) if err == nil { return res, nil } return action(f.backup) } // Create creates a file in the filesystem, returning the file and an error, if any happens. func (f *FallbackFs) Create(name string) (afero.File, error) { return f.doFile(func(fs afero.Fs) (afero.File, error) { return fs.Create(name) }) } // Mkdir creates a directory in the filesystem, return an error if any happens. func (f *FallbackFs) Mkdir(name string, perm os.FileMode) error { return f.do(func(fs afero.Fs) error { return fs.Mkdir(name, perm) }) } // MkdirAll creates a directory path and all parents that does not exist yet. func (f *FallbackFs) MkdirAll(path string, perm os.FileMode) error { return f.do(func(fs afero.Fs) error { return fs.MkdirAll(path, perm) }) } // Open opens a file, returning it or an error, if any happens. func (f *FallbackFs) Open(name string) (afero.File, error) { return f.doFile(func(fs afero.Fs) (afero.File, error) { return fs.Open(name) }) } // OpenFile opens a file using the given flags and the given mode. func (f *FallbackFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { return f.doFile(func(fs afero.Fs) (afero.File, error) { return fs.OpenFile(name, flag, perm) }) } // Remove removes a file identified by name, returning an error, if any happens. func (f *FallbackFs) Remove(name string) error { return f.do(func(fs afero.Fs) error { return fs.Remove(name) }) } // RemoveAll removes a directory path and any children it contains. It does not fail if the path does not exist (return nil). func (f *FallbackFs) RemoveAll(path string) error { return f.do(func(fs afero.Fs) error { return fs.RemoveAll(path) }) } // Rename renames a file. func (f *FallbackFs) Rename(oldname, newname string) error { return f.do(func(fs afero.Fs) error { return fs.Rename(oldname, newname) }) } // Stat returns a FileInfo describing the named file, or an error, if any happens. func (f *FallbackFs) Stat(name string) (os.FileInfo, error) { return f.doFileInfo(func(fs afero.Fs) (os.FileInfo, error) { return fs.Stat(name) }) } // Name returns the name of the file system. func (f *FallbackFs) Name() string { return "Fallback FS" } // Chmod changes the mode of the named file to mode. func (f *FallbackFs) Chmod(name string, mode os.FileMode) error { return f.do(func(fs afero.Fs) error { return fs.Chmod(name, mode) }) } // Chown changes the uid and gid of the named file. func (f *FallbackFs) Chown(name string, uid, gid int) error { return f.do(func(fs afero.Fs) error { return fs.Chown(name, uid, gid) }) } // Chtimes changes the access and modification times of the named file. func (f *FallbackFs) Chtimes(name string, atime time.Time, mtime time.Time) error { return f.do(func(fs afero.Fs) error { return fs.Chtimes(name, atime, mtime) }) }