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