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 }