hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

slice_fs.go (6187B)

    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 }