ADDED:
* stringsx:
** RunesInString
** RuneMapFromString
** SquashConsec
** SquashConsecRunes
** SquashConsecRunesAll
** SquashMap
** SquashWhitespace
This commit is contained in:
brent saner
2026-07-13 19:17:39 -04:00
parent 42c0e48081
commit c9f3e7a639
8 changed files with 1006 additions and 2 deletions
@@ -0,0 +1,360 @@
package main
import (
"io"
"io/fs"
"os"
"path/filepath"
"slices"
"r00t2.io/sysutils/paths"
)
/*
Append adds fsPaths as fs.FS to the MergedFS (before the embed.FS).
If fsPaths is empty, Append is a NO-OP.
noExist and dupes have the same meaning as in NewMergedFS.
*/
func (m *MergedFS) Append(fsPaths ...string) (noExist, dupes []string, err error) {
var exists bool
var fsPath string
var newPaths []string
var fsMem *realFsMember
var newFs []mergedFsMember
m.mut.Lock()
defer m.mut.Unlock()
newPaths = make([]string, 0, len(fsPaths))
newFs = make([]mergedFsMember, 0, len(fsPaths))
for _, fsPath = range fsPaths {
if exists, err = paths.RealPathExists(&fsPath); err != nil {
return
}
if !exists {
noExist = append(noExist, fsPath)
continue
}
if fsMem, err = NewRealFsMember(fsPath); err != nil {
return
}
if !m.uniq.Add(fsPath) {
dupes = append(dupes, fsPath)
continue
}
newFs = append(newFs, fsMem)
newPaths = append(newPaths, fsPath)
}
if len(newFs) == 0 {
return
}
m.overridePaths = append(m.overridePaths, newPaths...)
m.fses = append(m.fses[:len(m.fses)-1], append(newFs, m.fses[len(m.fses)-1])...)
return
}
// Close closes all realFsMember members. This MergedFS will not be able to be used after this is called.
func (m *MergedFS) Close() (err error) {
var fsMem mergedFsMember
m.mut.Lock()
defer m.mut.Unlock()
for _, fsMem = range m.fses {
if err = fsMem.(io.Closer).Close(); err != nil {
return
}
}
return
}
/*
Insert adds fsPaths as fs.FS to a MergedFS starting at index idx.
If fsPaths is empty, Insert is a NO-OP.
If idx is 0 or negative, they will be prepended.
If idx is larger than the last index of real directories (excluding the last embed.FS),
it will be reduced to the highest valid index.
noExist and dupes have the same meaning as in NewMergedFS.
*/
func (m *MergedFS) Insert(idx int, fsPaths ...string) (noExist, dupes []string, err error) {
var exists bool
var fsPath string
var newPaths []string
var fsMem *realFsMember
var newFs []mergedFsMember
if len(fsPaths) == 0 {
return
}
m.mut.Lock()
defer m.mut.Unlock()
newPaths = make([]string, 0, len(fsPaths))
newFs = make([]mergedFsMember, 0, len(fsPaths))
for _, fsPath = range fsPaths {
if exists, err = paths.RealPathExists(&fsPath); err != nil {
return
}
if !exists {
noExist = append(noExist, fsPath)
continue
}
if fsMem, err = NewRealFsMember(fsPath); err != nil {
return
}
if !m.uniq.Add(fsPath) {
dupes = append(dupes, fsPath)
continue
}
newPaths = append(newPaths, fsPath)
newFs = append(newFs, fsMem)
}
if len(newFs) == 0 {
return
}
if idx < 0 {
m.fses = append(newFs, m.fses...)
m.overridePaths = append(newPaths, m.overridePaths...)
return
} else if idx >= len(m.fses)-1 {
idx = len(m.fses) - 2
}
m.fses = append(m.fses[:idx], append(newFs, m.fses[idx:]...)...)
m.overridePaths = append(m.overridePaths[:idx], append(newPaths, m.overridePaths[idx:]...)...)
return
}
// Open opens filename fnm and returns it as file handler fh.
func (m *MergedFS) Open(fnm string) (fh fs.File, err error) {
var fsref fs.FS
m.mut.RLock()
defer m.mut.RUnlock()
// Iterate until a match is found.
for _, fsref = range m.fses {
if fh, err = fsref.Open(fnm); err == nil {
return
}
}
// No match found.
err = &fs.PathError{
Op: "Open",
Path: fnm,
Err: fs.ErrNotExist,
}
return
}
/*
Paths returns a slice of real filesystem paths that are used by this MergedFS.
If sorted is true, they will be returned as a sorted slice -
otherwise they will be returned in the order they are checked.
*/
func (m *MergedFS) Paths(sorted bool) (fsPaths []string) {
m.mut.RLock()
defer m.mut.RUnlock()
if sorted {
fsPaths = m.uniq.ToSlice()
slices.Sort(fsPaths)
} else {
fsPaths = make([]string, len(m.overridePaths)-1)
copy(fsPaths, m.overridePaths[:len(m.overridePaths)-1])
}
return
}
/*
ReadDir returns a listing of path matcher fpath.
The result is a combination of all filesystems; the first encountered matching
name is added.
*/
func (m *MergedFS) ReadDir(fpath string) (entries []fs.DirEntry, err error) {
var idx int
var e fs.DirEntry
var tmpEntries []fs.DirEntry
var seen map[string]bool = make(map[string]bool)
m.mut.RLock()
defer m.mut.RUnlock()
// merge real and embedded, preferring first-seen real paths
for idx, _ = range m.fses {
if m.overridePaths[idx] == "" {
if tmpEntries, err = m.fses[idx].ReadDir(filepath.FromSlash(fpath)); err != nil {
continue
}
} else {
if tmpEntries, err = os.ReadDir(filepath.Join(m.overridePaths[idx], filepath.FromSlash(fpath))); err != nil {
continue
}
}
for _, e = range tmpEntries {
if seen[e.Name()] {
continue
}
seen[e.Name()] = true
entries = append(entries, e)
}
}
return
}
/*
ReadFile returns the bytes for the first found fpath.
It wraps MergedFS.Open.
*/
func (m *MergedFS) ReadFile(fpath string) (b []byte, err error) {
var fh fs.File
if fh, err = m.Open(fpath); nil != err {
return
}
defer func() {
if fh != nil {
if err = fh.Close(); err != nil {
return
}
}
}()
if b, err = io.ReadAll(fh); err != nil {
return
}
return
}
/*
Stat returns the fs.FileInfo for the first found fpath.
It wraps MergedFS.Open.
*/
func (m *MergedFS) Stat(fpath string) (stat fs.FileInfo, err error) {
var fh fs.File
if fh, err = m.Open(fpath); nil != err {
return
}
defer func() {
if fh != nil {
if err = fh.Close(); err != nil {
return
}
}
}()
if stat, err = fh.Stat(); err != nil {
return
}
return
}
/*
RemoveFsByIdx removes the filesystem found at index idx.
An attempt to remove the final embed.FS filesystem will result in an ErrFsIdxOOB.
If idx is larger than the last index of "real" filesystems, an ErrFsIdxOOB will be returned.
*/
func (m *MergedFS) RemoveFsByIdx(idx uint) (err error) {
var fsPath string
m.mut.Lock()
defer m.mut.Unlock()
if idx+1 == uint(len(m.fses)) {
return
}
if idx > uint(len(m.overridePaths)-1) {
err = ErrFsIdxOOB
return
}
fsPath = m.overridePaths[idx]
if m.uniq.Contains(fsPath) {
m.uniq.Remove(fsPath)
}
m.fses = append(m.fses[:idx], m.fses[idx+1:]...)
m.overridePaths = append(m.overridePaths[:idx], m.overridePaths[idx+1:]...)
return
}
/*
RemoveFsByPath removes the fileststem with path dirPath.
If dirpath is empty, this function NOOPs.
If dirpath does not exist in this MergedFS, an ErrFsPathNoExists will be returned.
*/
func (m *MergedFS) RemoveFsByPath(dirPath string) (err error) {
var idx int
var fsp string
var p string = dirPath
if p == "" {
return
}
if err = paths.RealPath(&p); err != nil {
return
}
m.mut.Lock()
defer m.mut.Unlock()
if !m.uniq.Contains(p) {
err = ErrFsPathNoExists
return
}
m.uniq.Remove(p)
for idx, fsp = range m.overridePaths {
if fsp == p {
m.fses = append(m.fses[:idx], m.fses[idx+1:]...)
m.overridePaths = append(m.overridePaths[:idx], m.overridePaths[idx+1:]...)
return
}
}
err = ErrFsIdxOOB
return
}