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:
parent
524cf8e27b
commit
fc6b979a46
|
@ -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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue