github.com/SuCicada/su-hugo@v1.0.0/hugofs/slice_fs.go (about)

     1  // Copyright 2019 The Hugo Authors. 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  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package hugofs
    15  
    16  import (
    17  	"fmt"
    18  	"os"
    19  	"syscall"
    20  	"time"
    21  
    22  	"errors"
    23  
    24  	"github.com/spf13/afero"
    25  )
    26  
    27  var (
    28  	_ afero.Fs             = (*SliceFs)(nil)
    29  	_ afero.Lstater        = (*SliceFs)(nil)
    30  	_ FilesystemsUnwrapper = (*SliceFs)(nil)
    31  	_ afero.File           = (*sliceDir)(nil)
    32  )
    33  
    34  func NewSliceFs(dirs ...FileMetaInfo) (afero.Fs, error) {
    35  	if len(dirs) == 0 {
    36  		return NoOpFs, nil
    37  	}
    38  
    39  	for _, dir := range dirs {
    40  		if !dir.IsDir() {
    41  			return nil, errors.New("this fs supports directories only")
    42  		}
    43  	}
    44  
    45  	fs := &SliceFs{
    46  		dirs: dirs,
    47  	}
    48  
    49  	return fs, nil
    50  }
    51  
    52  // SliceFs is an ordered composite filesystem.
    53  type SliceFs struct {
    54  	dirs []FileMetaInfo
    55  }
    56  
    57  func (fs *SliceFs) UnwrapFilesystems() []afero.Fs {
    58  	var fss []afero.Fs
    59  	for _, dir := range fs.dirs {
    60  		fss = append(fss, dir.Meta().Fs)
    61  	}
    62  	return fss
    63  }
    64  
    65  func (fs *SliceFs) Chmod(n string, m os.FileMode) error {
    66  	return syscall.EPERM
    67  }
    68  
    69  func (fs *SliceFs) Chtimes(n string, a, m time.Time) error {
    70  	return syscall.EPERM
    71  }
    72  
    73  func (fs *SliceFs) Chown(n string, uid, gid int) error {
    74  	return syscall.EPERM
    75  }
    76  
    77  func (fs *SliceFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
    78  	fi, _, err := fs.pickFirst(name)
    79  	if err != nil {
    80  		return nil, false, err
    81  	}
    82  
    83  	if fi.IsDir() {
    84  		return decorateFileInfo(fi, fs, fs.getOpener(name), "", "", nil), false, nil
    85  	}
    86  
    87  	return nil, false, fmt.Errorf("lstat: files not supported: %q", name)
    88  }
    89  
    90  func (fs *SliceFs) Mkdir(n string, p os.FileMode) error {
    91  	return syscall.EPERM
    92  }
    93  
    94  func (fs *SliceFs) MkdirAll(n string, p os.FileMode) error {
    95  	return syscall.EPERM
    96  }
    97  
    98  func (fs *SliceFs) Name() string {
    99  	return "SliceFs"
   100  }
   101  
   102  func (fs *SliceFs) Open(name string) (afero.File, error) {
   103  	fi, idx, err := fs.pickFirst(name)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  
   108  	if !fi.IsDir() {
   109  		panic("currently only dirs in here")
   110  	}
   111  
   112  	return &sliceDir{
   113  		lfs:     fs,
   114  		idx:     idx,
   115  		dirname: name,
   116  	}, nil
   117  }
   118  
   119  func (fs *SliceFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) {
   120  	panic("not implemented")
   121  }
   122  
   123  func (fs *SliceFs) ReadDir(name string) ([]os.FileInfo, error) {
   124  	panic("not implemented")
   125  }
   126  
   127  func (fs *SliceFs) Remove(n string) error {
   128  	return syscall.EPERM
   129  }
   130  
   131  func (fs *SliceFs) RemoveAll(p string) error {
   132  	return syscall.EPERM
   133  }
   134  
   135  func (fs *SliceFs) Rename(o, n string) error {
   136  	return syscall.EPERM
   137  }
   138  
   139  func (fs *SliceFs) Stat(name string) (os.FileInfo, error) {
   140  	fi, _, err := fs.LstatIfPossible(name)
   141  	return fi, err
   142  }
   143  
   144  func (fs *SliceFs) Create(n string) (afero.File, error) {
   145  	return nil, syscall.EPERM
   146  }
   147  
   148  func (fs *SliceFs) getOpener(name string) func() (afero.File, error) {
   149  	return func() (afero.File, error) {
   150  		return fs.Open(name)
   151  	}
   152  }
   153  
   154  func (fs *SliceFs) pickFirst(name string) (os.FileInfo, int, error) {
   155  	for i, mfs := range fs.dirs {
   156  		meta := mfs.Meta()
   157  		fs := meta.Fs
   158  		fi, _, err := lstatIfPossible(fs, name)
   159  		if err == nil {
   160  			// Gotta match!
   161  			return fi, i, nil
   162  		}
   163  
   164  		if !os.IsNotExist(err) {
   165  			// Real error
   166  			return nil, -1, err
   167  		}
   168  	}
   169  
   170  	// Not found
   171  	return nil, -1, os.ErrNotExist
   172  }
   173  
   174  func (fs *SliceFs) readDirs(name string, startIdx, count int) ([]os.FileInfo, error) {
   175  	collect := func(lfs *FileMeta) ([]os.FileInfo, error) {
   176  		d, err := lfs.Fs.Open(name)
   177  		if err != nil {
   178  			if !os.IsNotExist(err) {
   179  				return nil, err
   180  			}
   181  			return nil, nil
   182  		} else {
   183  			defer d.Close()
   184  			dirs, err := d.Readdir(-1)
   185  			if err != nil {
   186  				return nil, err
   187  			}
   188  			return dirs, nil
   189  		}
   190  	}
   191  
   192  	var dirs []os.FileInfo
   193  
   194  	for i := startIdx; i < len(fs.dirs); i++ {
   195  		mfs := fs.dirs[i]
   196  
   197  		fis, err := collect(mfs.Meta())
   198  		if err != nil {
   199  			return nil, err
   200  		}
   201  
   202  		dirs = append(dirs, fis...)
   203  
   204  	}
   205  
   206  	seen := make(map[string]bool)
   207  	var duplicates []int
   208  	for i, fi := range dirs {
   209  		if !fi.IsDir() {
   210  			continue
   211  		}
   212  
   213  		if seen[fi.Name()] {
   214  			duplicates = append(duplicates, i)
   215  		} else {
   216  			// Make sure it's opened by this filesystem.
   217  			dirs[i] = decorateFileInfo(fi, fs, fs.getOpener(fi.(FileMetaInfo).Meta().Filename), "", "", nil)
   218  			seen[fi.Name()] = true
   219  		}
   220  	}
   221  
   222  	// Remove duplicate directories, keep first.
   223  	if len(duplicates) > 0 {
   224  		for i := len(duplicates) - 1; i >= 0; i-- {
   225  			idx := duplicates[i]
   226  			dirs = append(dirs[:idx], dirs[idx+1:]...)
   227  		}
   228  	}
   229  
   230  	if count > 0 && len(dirs) >= count {
   231  		return dirs[:count], nil
   232  	}
   233  
   234  	return dirs, nil
   235  }
   236  
   237  type sliceDir struct {
   238  	lfs     *SliceFs
   239  	idx     int
   240  	dirname string
   241  }
   242  
   243  func (f *sliceDir) Close() error {
   244  	return nil
   245  }
   246  
   247  func (f *sliceDir) Name() string {
   248  	return f.dirname
   249  }
   250  
   251  func (f *sliceDir) Read(p []byte) (n int, err error) {
   252  	panic("not implemented")
   253  }
   254  
   255  func (f *sliceDir) ReadAt(p []byte, off int64) (n int, err error) {
   256  	panic("not implemented")
   257  }
   258  
   259  func (f *sliceDir) Readdir(count int) ([]os.FileInfo, error) {
   260  	return f.lfs.readDirs(f.dirname, f.idx, count)
   261  }
   262  
   263  func (f *sliceDir) Readdirnames(count int) ([]string, error) {
   264  	dirsi, err := f.Readdir(count)
   265  	if err != nil {
   266  		return nil, err
   267  	}
   268  
   269  	dirs := make([]string, len(dirsi))
   270  	for i, d := range dirsi {
   271  		dirs[i] = d.Name()
   272  	}
   273  	return dirs, nil
   274  }
   275  
   276  func (f *sliceDir) Seek(offset int64, whence int) (int64, error) {
   277  	panic("not implemented")
   278  }
   279  
   280  func (f *sliceDir) Stat() (os.FileInfo, error) {
   281  	panic("not implemented")
   282  }
   283  
   284  func (f *sliceDir) Sync() error {
   285  	panic("not implemented")
   286  }
   287  
   288  func (f *sliceDir) Truncate(size int64) error {
   289  	panic("not implemented")
   290  }
   291  
   292  func (f *sliceDir) Write(p []byte) (n int, err error) {
   293  	panic("not implemented")
   294  }
   295  
   296  func (f *sliceDir) WriteAt(p []byte, off int64) (n int, err error) {
   297  	panic("not implemented")
   298  }
   299  
   300  func (f *sliceDir) WriteString(s string) (ret int, err error) {
   301  	panic("not implemented")
   302  }