mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-04-20 16:40:29 +00:00
92 lines
2.1 KiB
Go
92 lines
2.1 KiB
Go
package appview
|
|
|
|
import (
|
|
"io/fs"
|
|
"sort"
|
|
)
|
|
|
|
// overlayFS layers two fs.FS instances: upper wins, lower is fallback.
|
|
// ReadDir merges entries from both layers (upper entries replace lower).
|
|
type overlayFS struct {
|
|
upper fs.FS
|
|
lower fs.FS
|
|
}
|
|
|
|
// newOverlayFS creates a layered filesystem. Files in upper take priority over lower.
|
|
func newOverlayFS(upper, lower fs.FS) fs.FS {
|
|
return &overlayFS{upper: upper, lower: lower}
|
|
}
|
|
|
|
func (o *overlayFS) Open(name string) (fs.File, error) {
|
|
f, err := o.upper.Open(name)
|
|
if err == nil {
|
|
return f, nil
|
|
}
|
|
return o.lower.Open(name)
|
|
}
|
|
|
|
// ReadFile implements fs.ReadFileFS for efficient file reads.
|
|
func (o *overlayFS) ReadFile(name string) ([]byte, error) {
|
|
if rfs, ok := o.upper.(fs.ReadFileFS); ok {
|
|
data, err := rfs.ReadFile(name)
|
|
if err == nil {
|
|
return data, nil
|
|
}
|
|
}
|
|
if rfs, ok := o.lower.(fs.ReadFileFS); ok {
|
|
return rfs.ReadFile(name)
|
|
}
|
|
// Fallback: open and read
|
|
f, err := o.Open(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
stat, err := f.Stat()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
buf := make([]byte, stat.Size())
|
|
_, err = f.(interface{ Read([]byte) (int, error) }).Read(buf)
|
|
return buf, err
|
|
}
|
|
|
|
// ReadDir implements fs.ReadDirFS with merge semantics: entries from upper
|
|
// replace entries from lower with the same name.
|
|
func (o *overlayFS) ReadDir(name string) ([]fs.DirEntry, error) {
|
|
merged := make(map[string]fs.DirEntry)
|
|
|
|
// Read lower first
|
|
if rdfs, ok := o.lower.(fs.ReadDirFS); ok {
|
|
entries, err := rdfs.ReadDir(name)
|
|
if err == nil {
|
|
for _, e := range entries {
|
|
merged[e.Name()] = e
|
|
}
|
|
}
|
|
}
|
|
|
|
// Upper entries override lower
|
|
if rdfs, ok := o.upper.(fs.ReadDirFS); ok {
|
|
entries, err := rdfs.ReadDir(name)
|
|
if err == nil {
|
|
for _, e := range entries {
|
|
merged[e.Name()] = e
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(merged) == 0 {
|
|
return nil, &fs.PathError{Op: "readdir", Path: name, Err: fs.ErrNotExist}
|
|
}
|
|
|
|
result := make([]fs.DirEntry, 0, len(merged))
|
|
for _, e := range merged {
|
|
result = append(result, e)
|
|
}
|
|
sort.Slice(result, func(i, j int) bool {
|
|
return result[i].Name() < result[j].Name()
|
|
})
|
|
return result, nil
|
|
}
|