Answer architecture round 11: a source states its mount in a field its manifest, its labels and its file reads share, so the mount can come before or after the manifest; the README says a stand-in for the places is a record with a format

This commit is contained in:
2026-10-11 15:13:39 +02:00
committed by Lillem4n
parent ad1dfac447
commit e8f5fd47d5
4 changed files with 29 additions and 20 deletions
+10 -7
View File
@@ -13,11 +13,13 @@ import (
)
// Source is one tree to load, from the root of an fs.FS. label prefixes file names in
// errors; onDisk marks label as a directory that must exist.
// errors; onDisk marks label as a directory that must exist; at is the folder fsys mounts the
// tree at, "" for its root.
type Source struct {
fsys fs.FS
label string
onDisk bool
at string
}
// Dir is the tree in the directory path, its errors labelled with it.
@@ -30,8 +32,9 @@ func FS(fsys fs.FS) Source { return Source{fsys: fsys} }
// Mounted is s with its tree under the folders the dot path namespace spells, "" for none.
func (s Source) Mounted(namespace string) Source {
if namespace != "" {
s.fsys = mounted{fsys: s.fsys, at: strings.ReplaceAll(namespace, ".", "/")}
if namespace != "" && s.at == "" {
s.at = strings.ReplaceAll(namespace, ".", "/")
s.fsys = mounted{fsys: s.fsys, at: s.at}
}
return s
}
@@ -48,14 +51,14 @@ type Category struct {
// ReadRows reads a rows file beside the category.
func (c Category) ReadRows(name string) (string, error) { return c.rows.read(name) }
// labelled is the file p as errors name it: under the source's label, as the labelled tree
// spells it, below any mount.
// labelled is the file p as errors name it: a labelled source's as its own tree spells it,
// below the mount.
func (s Source) labelled(p string) string {
if s.label == "" {
return p
}
if m, isMounted := s.fsys.(mounted); isMounted {
p, _ = m.inner(p)
if inner, below := underMount(s.at, p); below {
p = inner
}
return path.Join(s.label, p)
}
+2 -7
View File
@@ -35,7 +35,7 @@ func (s Source) Manifest() (Manifest, error) {
if err := s.check(); err != nil {
return Manifest{}, err
}
b, err := fs.ReadFile(s.fsys, ManifestFile)
b, err := fs.ReadFile(s.fsys, path.Join(s.at, ManifestFile))
if errors.Is(err, fs.ErrNotExist) {
return Manifest{}, nil
}
@@ -50,12 +50,7 @@ func (s Source) Manifest() (Manifest, error) {
}
// ManifestPath is the manifest's path as errors name it.
func (s Source) ManifestPath() string {
if m, isMounted := s.fsys.(mounted); isMounted {
return s.labelled(path.Join(m.at, ManifestFile))
}
return s.labelled(ManifestFile)
}
func (s Source) ManifestPath() string { return s.labelled(path.Join(s.at, ManifestFile)) }
// decodeManifest decodes a manifest, refusing any key or shape it does not define, and
// reports every mistake: first each entry's own, in path order, then each parent naming
+14 -3
View File
@@ -45,11 +45,22 @@ func (m mounted) ReadDir(name string) ([]fs.DirEntry, error) {
// inner is name as fsys spells it, where name lies at or below at.
func (m mounted) inner(name string) (string, bool) {
if name == m.at {
inner, below := underMount(m.at, name)
return inner, below && fs.ValidPath(name)
}
// underMount is name as the tree mounted at at spells it, where name lies at or below at.
func underMount(at, name string) (string, bool) {
switch name {
case at:
return ".", true
case "":
return name, false
}
rest, below := strings.CutPrefix(name, m.at+"/")
return rest, below && fs.ValidPath(name)
if at == "" {
return name, true
}
return strings.CutPrefix(name, at+"/")
}
// towards is the folder below name on the way to at, where name lies above at.