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 }