Update implementation of utils.DirFS

It is now patterned after the Go 1.20 implementation, in order to better represent the current state of things in a proposal I am making.
This commit is contained in:
Ben Visness 2023-04-08 11:41:02 -05:00
parent 524cf8e27b
commit fc6b979a46
1 changed files with 60 additions and 24 deletions

View File

@ -1,6 +1,7 @@
package utils package utils
import ( import (
"errors"
"io/fs" "io/fs"
"os" "os"
"runtime" "runtime"
@ -18,55 +19,90 @@ import (
// contains arbitrary content. // contains arbitrary content.
// //
// The result implements fs.StatFS AND fs.ReadDirFS because god dammit why not. // The result implements fs.StatFS AND fs.ReadDirFS because god dammit why not.
//
// Implementation copy-pasted from Go 1.20.2.
func DirFS(dir string) fs.FS { func DirFS(dir string) fs.FS {
return dirFS(dir) return dirFS(dir)
} }
func containsAny(s, chars string) bool {
for i := 0; i < len(s); i++ {
for j := 0; j < len(chars); j++ {
if s[i] == chars[j] {
return true
}
}
}
return false
}
type dirFS string type dirFS string
var _ fs.StatFS = dirFS("") var _ fs.StatFS = dirFS("")
var _ fs.ReadDirFS = dirFS("") var _ fs.ReadDirFS = dirFS("")
func (dir dirFS) Open(name string) (fs.File, error) { func (dir dirFS) Open(name string) (fs.File, error) {
if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) { fullname, err := dir.join(name)
return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrInvalid}
}
f, err := os.Open(string(dir) + "/" + name)
if err != nil { if err != nil {
return nil, err // nil fs.File return nil, &os.PathError{Op: "stat", Path: name, Err: err}
}
f, err := os.Open(fullname)
if err != nil {
// DirFS takes a string appropriate for GOOS,
// while the name argument here is always slash separated.
// dir.join will have mixed the two; undo that for
// error reporting.
err.(*os.PathError).Path = name
return nil, err
} }
return f, nil return f, nil
} }
func (dir dirFS) Stat(name string) (fs.FileInfo, error) { func (dir dirFS) Stat(name string) (fs.FileInfo, error) {
if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) { fullname, err := dir.join(name)
return nil, &os.PathError{Op: "stat", Path: name, Err: os.ErrInvalid}
}
f, err := os.Stat(string(dir) + "/" + name)
if err != nil { if err != nil {
return nil, &os.PathError{Op: "stat", Path: name, Err: err}
}
f, err := os.Stat(fullname)
if err != nil {
// See comment in dirFS.Open.
err.(*os.PathError).Path = name
return nil, err return nil, err
} }
return f, nil return f, nil
} }
func (dir dirFS) ReadDir(name string) ([]fs.DirEntry, error) { func (dir dirFS) ReadDir(name string) ([]fs.DirEntry, error) {
if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) { fullname, err := dir.join(name)
return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrInvalid}
}
d, err := os.ReadDir(string(dir) + "/" + name)
if err != nil { if err != nil {
return nil, &os.PathError{Op: "stat", Path: name, Err: err}
}
d, err := os.ReadDir(fullname)
if err != nil {
// See comment in dirFS.Open.
err.(*os.PathError).Path = name
return nil, err return nil, err
} }
return d, nil return d, nil
} }
func fromFS(path string) (string, error) {
if runtime.GOOS == "plan9" {
if len(path) > 0 && path[0] == '#' {
return "", os.ErrInvalid
}
}
for i := range path {
if path[i] == 0 {
return "", os.ErrInvalid
}
}
return path, nil
}
// join returns the path for name in dir.
func (dir dirFS) join(name string) (string, error) {
if dir == "" {
return "", errors.New("os: DirFS with empty root")
}
if !fs.ValidPath(name) {
return "", os.ErrInvalid
}
name, err := fromFS(name)
if err != nil {
return "", os.ErrInvalid
}
if os.IsPathSeparator(dir[len(dir)-1]) {
return string(dir) + name, nil
}
return string(dir) + string(os.PathSeparator) + name, nil
}