From e8f5fd47d55acf52e71a6a1369968c647a26648f Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sun, 11 Oct 2026 15:13:39 +0200 Subject: [PATCH] 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 --- README.md | 6 +++--- internal/datafiles/datafiles.go | 17 ++++++++++------- internal/datafiles/manifest.go | 9 ++------- internal/datafiles/mount.go | 17 ++++++++++++++--- 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 4766d48..426856c 100644 --- a/README.md +++ b/README.md @@ -372,9 +372,9 @@ f, err := fejkdata.New(fejkdata.WithDataFS(svse.Modules()...)) ``` Two locales are `WithDataFS(append(svse.Modules(), enus.Modules()...)...)`. To stand in your -own places, pass the locale's `FS` alone beside a tree providing `geo.SE.address` with the -columns that port, `street`, `street-number`, `postal-code` and `locality`: -`WithDataFS(svse.FS, myPlaces)`. `New` fails where no `WithDataFS` or `WithDataPath` names the +own places, pass the locale's `FS` alone beside a tree providing `geo.SE.address`, a record +with a `format` and the columns `sv_SE.address` reads, `street`, `street-number`, +`postal-code` and `locality`: `WithDataFS(svse.FS, myPlaces)`. `New` fails where no `WithDataFS` or `WithDataPath` names the data to load. `New` refuses data that breaks the grammar, could mean two things or cannot render a valid diff --git a/internal/datafiles/datafiles.go b/internal/datafiles/datafiles.go index be80b32..344e613 100644 --- a/internal/datafiles/datafiles.go +++ b/internal/datafiles/datafiles.go @@ -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) } diff --git a/internal/datafiles/manifest.go b/internal/datafiles/manifest.go index 9313261..f95c0a2 100644 --- a/internal/datafiles/manifest.go +++ b/internal/datafiles/manifest.go @@ -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 diff --git a/internal/datafiles/mount.go b/internal/datafiles/mount.go index 560848a..3b675be 100644 --- a/internal/datafiles/mount.go +++ b/internal/datafiles/mount.go @@ -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.