package fstest
import (
"io"
"io/fs"
"path"
"slices"
"strings"
"time"
)
type MapFS map [string ]*MapFile
type MapFile struct {
Data []byte
Mode fs .FileMode
ModTime time .Time
Sys any
}
var _ fs .FS = MapFS (nil )
var _ fs .File = (*openMapFile )(nil )
func (fsys MapFS ) Open (name string ) (fs .File , error ) {
if !fs .ValidPath (name ) {
return nil , &fs .PathError {Op : "open" , Path : name , Err : fs .ErrNotExist }
}
file := fsys [name ]
if file != nil && file .Mode &fs .ModeDir == 0 {
return &openMapFile {name , mapFileInfo {path .Base (name ), file }, 0 }, nil
}
var list []mapFileInfo
var elem string
var need = make (map [string ]bool )
if name == "." {
elem = "."
for fname , f := range fsys {
i := strings .Index (fname , "/" )
if i < 0 {
if fname != "." {
list = append (list , mapFileInfo {fname , f })
}
} else {
need [fname [:i ]] = true
}
}
} else {
elem = name [strings .LastIndex (name , "/" )+1 :]
prefix := name + "/"
for fname , f := range fsys {
if strings .HasPrefix (fname , prefix ) {
felem := fname [len (prefix ):]
i := strings .Index (felem , "/" )
if i < 0 {
list = append (list , mapFileInfo {felem , f })
} else {
need [fname [len (prefix ):len (prefix )+i ]] = true
}
}
}
if file == nil && list == nil && len (need ) == 0 {
return nil , &fs .PathError {Op : "open" , Path : name , Err : fs .ErrNotExist }
}
}
for _ , fi := range list {
delete (need , fi .name )
}
for name := range need {
list = append (list , mapFileInfo {name , &MapFile {Mode : fs .ModeDir | 0555 }})
}
slices .SortFunc (list , func (a , b mapFileInfo ) int {
return strings .Compare (a .name , b .name )
})
if file == nil {
file = &MapFile {Mode : fs .ModeDir | 0555 }
}
return &mapDir {name , mapFileInfo {elem , file }, list , 0 }, nil
}
type fsOnly struct { fs .FS }
func (fsys MapFS ) ReadFile (name string ) ([]byte , error ) {
return fs .ReadFile (fsOnly {fsys }, name )
}
func (fsys MapFS ) Stat (name string ) (fs .FileInfo , error ) {
return fs .Stat (fsOnly {fsys }, name )
}
func (fsys MapFS ) ReadDir (name string ) ([]fs .DirEntry , error ) {
return fs .ReadDir (fsOnly {fsys }, name )
}
func (fsys MapFS ) Glob (pattern string ) ([]string , error ) {
return fs .Glob (fsOnly {fsys }, pattern )
}
type noSub struct {
MapFS
}
func (noSub ) Sub () {}
func (fsys MapFS ) Sub (dir string ) (fs .FS , error ) {
return fs .Sub (noSub {fsys }, dir )
}
type mapFileInfo struct {
name string
f *MapFile
}
func (i *mapFileInfo ) Name () string { return path .Base (i .name ) }
func (i *mapFileInfo ) Size () int64 { return int64 (len (i .f .Data )) }
func (i *mapFileInfo ) Mode () fs .FileMode { return i .f .Mode }
func (i *mapFileInfo ) Type () fs .FileMode { return i .f .Mode .Type () }
func (i *mapFileInfo ) ModTime () time .Time { return i .f .ModTime }
func (i *mapFileInfo ) IsDir () bool { return i .f .Mode &fs .ModeDir != 0 }
func (i *mapFileInfo ) Sys () any { return i .f .Sys }
func (i *mapFileInfo ) Info () (fs .FileInfo , error ) { return i , nil }
func (i *mapFileInfo ) String () string {
return fs .FormatFileInfo (i )
}
type openMapFile struct {
path string
mapFileInfo
offset int64
}
func (f *openMapFile ) Stat () (fs .FileInfo , error ) { return &f .mapFileInfo , nil }
func (f *openMapFile ) Close () error { return nil }
func (f *openMapFile ) Read (b []byte ) (int , error ) {
if f .offset >= int64 (len (f .f .Data )) {
return 0 , io .EOF
}
if f .offset < 0 {
return 0 , &fs .PathError {Op : "read" , Path : f .path , Err : fs .ErrInvalid }
}
n := copy (b , f .f .Data [f .offset :])
f .offset += int64 (n )
return n , nil
}
func (f *openMapFile ) Seek (offset int64 , whence int ) (int64 , error ) {
switch whence {
case 0 :
case 1 :
offset += f .offset
case 2 :
offset += int64 (len (f .f .Data ))
}
if offset < 0 || offset > int64 (len (f .f .Data )) {
return 0 , &fs .PathError {Op : "seek" , Path : f .path , Err : fs .ErrInvalid }
}
f .offset = offset
return offset , nil
}
func (f *openMapFile ) ReadAt (b []byte , offset int64 ) (int , error ) {
if offset < 0 || offset > int64 (len (f .f .Data )) {
return 0 , &fs .PathError {Op : "read" , Path : f .path , Err : fs .ErrInvalid }
}
n := copy (b , f .f .Data [offset :])
if n < len (b ) {
return n , io .EOF
}
return n , nil
}
type mapDir struct {
path string
mapFileInfo
entry []mapFileInfo
offset int
}
func (d *mapDir ) Stat () (fs .FileInfo , error ) { return &d .mapFileInfo , nil }
func (d *mapDir ) Close () error { return nil }
func (d *mapDir ) Read (b []byte ) (int , error ) {
return 0 , &fs .PathError {Op : "read" , Path : d .path , Err : fs .ErrInvalid }
}
func (d *mapDir ) ReadDir (count int ) ([]fs .DirEntry , error ) {
n := len (d .entry ) - d .offset
if n == 0 && count > 0 {
return nil , io .EOF
}
if count > 0 && n > count {
n = count
}
list := make ([]fs .DirEntry , n )
for i := range list {
list [i ] = &d .entry [d .offset +i ]
}
d .offset += n
return list , nil
}
The pages are generated with Golds v0.6.9-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .