package wpk
import (
"errors"
"io"
"io/fs"
"os"
"path"
"regexp"
"strings"
)
// MakeTagsPath receives file path and returns it with ".wpt" extension.
// It hepls to open splitted package.
func MakeTagsPath(fpath string) string {
var ext = path.Ext(fpath)
return fpath[:len(fpath)-len(ext)] + ".wpt"
}
// MakeDataPath receives file path and returns it with ".wpf" extension.
// It hepls to open splitted package.
func MakeDataPath(fpath string) string {
var ext = path.Ext(fpath)
return fpath[:len(fpath)-len(ext)] + ".wpf"
}
// PackDirFile is a directory file whose entries can be read with the ReadDir method.
// fs.ReadDirFile interface implementation.
type PackDirFile struct {
*TagsetRaw // has fs.FileInfo interface
ftt *FTT
}
// fs.ReadDirFile interface implementation.
func (f *PackDirFile) Stat() (fs.FileInfo, error) {
return f, nil
}
// fs.ReadDirFile interface implementation.
func (f *PackDirFile) Read(b []byte) (n int, err error) {
return 0, io.EOF
}
// fs.ReadDirFile interface implementation.
func (f *PackDirFile) Close() error {
return nil
}
// fs.ReadDirFile interface implementation.
func (f *PackDirFile) ReadDir(n int) (matches []fs.DirEntry, err error) {
return f.ftt.ReadDirN(f.Path(), n)
}
var (
evlre = regexp.MustCompile(`\$\w+`) // env var with linux-like syntax
evure = regexp.MustCompile(`\$\{\w+\}`) // env var with unix-like syntax
evwre = regexp.MustCompile(`\%\w+\%`) // env var with windows-like syntax
)
// Envfmt replaces environment variables entries in file path to there values.
// Environment variables must be enclosed as ${...} in string.
func Envfmt(p string) string {
return evwre.ReplaceAllStringFunc(evure.ReplaceAllStringFunc(evlre.ReplaceAllStringFunc(p, func(name string) string {
// strip $VAR and replace by environment value
if val, ok := os.LookupEnv(name[1:]); ok {
return val
} else {
return name
}
}), func(name string) string {
// strip ${VAR} and replace by environment value
if val, ok := os.LookupEnv(name[2 : len(name)-1]); ok {
return val
} else {
return name
}
}), func(name string) string {
// strip %VAR% and replace by environment value
if val, ok := os.LookupEnv(name[1 : len(name)-1]); ok {
return val
} else {
return name
}
})
}
// PathExists check up file or directory existence.
func PathExists(path string) (bool, error) {
var err error
if _, err = os.Stat(path); err == nil {
return true, nil
}
if errors.Is(err, fs.ErrNotExist) {
return false, nil
}
return true, err
}
// TempPath returns filename located at temporary directory.
func TempPath(fname string) string {
return path.Join(ToSlash(os.TempDir()), fname)
}
// ToSlash brings filenames to true slashes
// without superfluous allocations if it possible.
func ToSlash(s string) string {
var b = S2B(s)
var bc = b
var c bool
for i, v := range b {
if v == '\\' {
if !c {
bc, c = []byte(s), true
}
bc[i] = '/'
}
}
return B2S(bc)
}
// Normalize brings file path to normalized form. Normalized path is the key to FTT map.
var Normalize = ToSlash
// ReadDirN returns fs.DirEntry array with nested into given package directory presentation.
// It's core function for ReadDirFile and ReadDirFS structures.
func (ftt *FTT) ReadDirN(fulldir string, n int) (list []fs.DirEntry, err error) {
var found = map[string]fs.DirEntry{}
var prefix string
if fulldir != "." && fulldir != "" {
prefix = Normalize(fulldir) + "/" // set terminated slash
}
ftt.rwm.Range(func(fkey string, ts *TagsetRaw) bool {
if strings.HasPrefix(fkey, prefix) {
var suffix = fkey[len(prefix):]
var sp = strings.IndexByte(suffix, '/')
if sp < 0 { // file detected
found[suffix] = ts
n--
} else { // dir detected
var subdir = path.Join(prefix, suffix[:sp])
if _, ok := found[subdir]; !ok {
var dts = MakeTagset(nil, 2, 2).
Put(TIDpath, StrTag(subdir))
var f = &PackDirFile{
TagsetRaw: dts,
ftt: ftt,
}
found[subdir] = f
n--
}
}
}
return n != 0
})
list = make([]fs.DirEntry, len(found))
var i int
for _, de := range found {
list[i] = de
i++
}
if n > 0 {
err = io.EOF
}
return
}
// OpenDir returns PackDirFile structure associated with group of files in package
// pooled with common directory prefix. Usable to implement fs.FileSystem interface.
func (ftt *FTT) OpenDir(fulldir string) (fs.ReadDirFile, error) {
var prefix string
if fulldir != "." && fulldir != "" {
prefix = Normalize(fulldir) + "/" // set terminated slash
}
var f *PackDirFile
ftt.rwm.Range(func(fkey string, ts *TagsetRaw) bool {
if strings.HasPrefix(fkey, prefix) {
var dts = MakeTagset(nil, 2, 2).
Put(TIDpath, StrTag(fulldir))
f = &PackDirFile{
TagsetRaw: dts,
ftt: ftt,
}
return false
}
return true
})
if f != nil {
return f, nil
}
// on case if not found
return nil, &fs.PathError{Op: "open", Path: fulldir, Err: fs.ErrNotExist}
}
// The End.