//go:build !noembed package bundled import ( "io/fs" "strings" "time" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs" ) const embedded = true const scheme = "bundled:///" func splitPath(path string) (rest string, ok bool) { return strings.CutPrefix(path, scheme) } func libPath() string { return scheme + "libs" } // wrappedFS is implemented directly rather than going through [io/fs.FS]. // Our vfs.FS works with file contents in terms of strings, and that's // what go:embed does under the hood, but going through fs.FS will cause // copying to []byte and back. type wrappedFS struct { fs vfs.FS } var _ vfs.FS = (*wrappedFS)(nil) func wrapFS(fs vfs.FS) vfs.FS { return &wrappedFS{fs: fs} } func (vfs *wrappedFS) UseCaseSensitiveFileNames() bool { return vfs.fs.UseCaseSensitiveFileNames() } func (vfs *wrappedFS) FileExists(path string) bool { if rest, ok := splitPath(path); ok { _, ok := embeddedContents[rest] return ok } return vfs.fs.FileExists(path) } func (vfs *wrappedFS) ReadFile(path string) (contents string, ok bool) { if rest, ok := splitPath(path); ok { contents, ok = embeddedContents[rest] return contents, ok } return vfs.fs.ReadFile(path) } func (vfs *wrappedFS) DirectoryExists(path string) bool { if rest, ok := splitPath(path); ok { return rest == "libs" } return vfs.fs.DirectoryExists(path) } func (vfs *wrappedFS) GetAccessibleEntries(path string) (result vfs.Entries) { if rest, ok := splitPath(path); ok { if rest == "" { result.Directories = []string{"libs"} } else if rest == "libs" { result.Files = LibNames } return result } return vfs.fs.GetAccessibleEntries(path) } var rootEntries = []fs.DirEntry{ fs.FileInfoToDirEntry(&fileInfo{name: "libs", mode: fs.ModeDir}), } func (vfs *wrappedFS) Stat(path string) vfs.FileInfo { if rest, ok := splitPath(path); ok { if rest == "" || rest == "libs" { return &fileInfo{name: rest, mode: fs.ModeDir} } if lib, ok := embeddedContents[rest]; ok { libName, _ := strings.CutPrefix(rest, "libs/") return &fileInfo{name: libName, size: int64(len(lib))} } return nil } return vfs.fs.Stat(path) } func (vfs *wrappedFS) WalkDir(root string, walkFn vfs.WalkDirFunc) error { if rest, ok := splitPath(root); ok { if err := vfs.walkDir(rest, walkFn); err != nil { if err == fs.SkipAll { //nolint:errorlint return nil } return err } return nil } return vfs.fs.WalkDir(root, walkFn) } func (vfs *wrappedFS) walkDir(rest string, walkFn vfs.WalkDirFunc) error { var entries []fs.DirEntry switch rest { case "": entries = rootEntries case "libs": entries = libsEntries default: return nil } for _, entry := range entries { name := rest + "/" + entry.Name() if err := walkFn(scheme+name, entry, nil); err != nil { if err == fs.SkipAll { //nolint:errorlint return fs.SkipAll } if err == fs.SkipDir { //nolint:errorlint continue } return err } if entry.IsDir() { if err := vfs.walkDir(name, walkFn); err != nil { return err } } } return nil } func (vfs *wrappedFS) Realpath(path string) string { if _, ok := splitPath(path); ok { return path } return vfs.fs.Realpath(path) } func (vfs *wrappedFS) WriteFile(path string, data string, writeByteOrderMark bool) error { if _, ok := splitPath(path); ok { panic("cannot write to embedded file system") } return vfs.fs.WriteFile(path, data, writeByteOrderMark) } func (vfs *wrappedFS) Remove(path string) error { if _, ok := splitPath(path); ok { panic("cannot remove from embedded file system") } return vfs.fs.Remove(path) } func (vfs *wrappedFS) Chtimes(path string, aTime time.Time, mTime time.Time) error { if _, ok := splitPath(path); ok { panic("cannot change times on embedded file system") } return vfs.fs.Chtimes(path, aTime, mTime) } type fileInfo struct { mode fs.FileMode name string size int64 } var ( _ fs.FileInfo = (*fileInfo)(nil) _ fs.DirEntry = (*fileInfo)(nil) ) func (fi *fileInfo) IsDir() bool { return fi.mode.IsDir() } func (fi *fileInfo) ModTime() time.Time { return time.Time{} } func (fi *fileInfo) Mode() fs.FileMode { return fi.mode } func (fi *fileInfo) Name() string { return fi.name } func (fi *fileInfo) Size() int64 { return fi.size } func (fi *fileInfo) Sys() any { return nil } func (fi *fileInfo) Info() (fs.FileInfo, error) { return fi, nil } func (fi *fileInfo) Type() fs.FileMode { return fi.mode.Type() }