Answer architecture round 2: each category site carries its source's default modules, set where the walk or the index loads it, and the note is written beside the resolve that finds the read; the decision states why New checks no reads

This commit is contained in:
2026-10-10 13:20:26 +02:00
committed by Lillem4n
parent 2ddba2ce96
commit 3da487f8ea
5 changed files with 42 additions and 55 deletions
+14 -8
View File
@@ -11,18 +11,22 @@ import (
// the unloaded categories it needs: docs/decisions.md#a-source-whose-manifest-carries-an-index-loads-each-category-on-first-reach-any-other-loads-in-new-with-what-it-reads
func loadSources(sources []datafiles.Source) (folder, error) {
root := folder{children: map[string]node{}}
defaultModules := map[string][]string{}
walked := map[string][]string{}
for _, src := range sources {
if err := addSource(&root, src, defaultModules); err != nil {
if err := addSource(&root, src, walked); err != nil {
return folder{}, err
}
}
return root, loadReached(&root, categorySites(&root), nil, defaultModules)
sites := categorySites(&root)
for i := range sites {
sites[i].defaultModules = walked[sites[i].path]
}
return root, loadReached(&root, sites, nil)
}
// addSource places src's categories in root, and sets in defaultModules, for each category
// it loads, the modules src reads by default.
func addSource(root *folder, src datafiles.Source, defaultModules map[string][]string) error {
// addSource places src's categories in root, and sets in walked, for each category it loads
// here, the modules src reads by default.
func addSource(root *folder, src datafiles.Source, walked map[string][]string) error {
m, err := src.Manifest()
if err != nil {
return err
@@ -36,7 +40,7 @@ func addSource(root *folder, src datafiles.Source, defaultModules map[string][]s
return err
}
for _, s := range categorySites(g) {
defaultModules[s.path] = m.Reads
walked[s.path] = m.Reads
}
mergeFolder(root, g)
return nil
@@ -83,11 +87,13 @@ func mergeFolder(dst, src *folder) {
// single inline node.
type nodeSet func(fn func(label string, n node) error) error
// categorySite is a loaded category, where it sits and its own dot path.
// categorySite is a loaded category, where it sits, its own dot path, and the modules its
// source reads by default.
type categorySite struct {
categoryAt
n node
path string
defaultModules []string
}
// categorySites lists every category under root, folders and names in sorted order.
+3 -2
View File
@@ -583,8 +583,9 @@ when a source carries it.
2026-09-24, Lilleman auf Larv; revised 2026-10-10, when the shipped data became Go packages.
Serves goal 6.2. `sv_SE.FS` loads alone, and `sv_SE.address` then fails at first reach,
naming `github.com/larvit/fejkdata/data/geo/SE`, which `sv_SE`'s manifest reads. `New`
checks no `reads`: a stand-in providing the same paths under another name would fail it
(goal 9.2).
checks no `reads`: a check by module name fails a stand-in providing the same paths (goal
9.2), and a check by path needs every reference, which only loading each indexed category
gives, against goal 13.
`--no-shipped-data -d` takes a locale's folder with its country's, or a set of one's own.
## The default embed holds every Swedish postort the import can place and give a street-delivery code and a street, and the US places of 25,000 or more
+4 -25
View File
@@ -60,7 +60,7 @@ func (f *Generator) loadCallerPath(path string) (string, []string, error) {
return "", nil, fmt.Errorf("fejkdata: %w", err)
}
if c, ok := categoryOn(&f.root, segs); ok && c.unloaded() {
if err := loadReached(&f.root, nil, []categoryAt{c}, nil); err != nil {
if err := loadReached(&f.root, nil, []categoryAt{c}); err != nil {
return "", nil, fmt.Errorf("fejkdata: %w", loadError{err})
}
}
@@ -107,9 +107,8 @@ func needs(root *folder, s categorySite) []categoryAt {
// loadReached loads the unloaded categories among wanted and every unloaded category they
// or sites need, then runs sites and all it loaded through a whole load's pipeline. On an
// error it puts back what it loaded, so the next call fails the same way. defaultModules
// holds, by path, the modules the source of each of sites reads by default.
func loadReached(root *folder, sites []categorySite, wanted []categoryAt, defaultModules map[string][]string) error {
// error it puts back what it loaded, so the next call fails the same way.
func loadReached(root *folder, sites []categorySite, wanted []categoryAt) error {
queue := wanted
for _, s := range sites {
queue = append(queue, needs(root, s)...)
@@ -120,14 +119,6 @@ func loadReached(root *folder, sites []categorySite, wanted []categoryAt, defaul
err = b.run(root, sites)
}
if err != nil {
err = withDefaultModules(err, func(category string) []string {
for _, l := range b {
if l.site.path == category {
return l.was.defaultModules
}
}
return defaultModules[category]
})
b.putBack()
}
return err
@@ -158,6 +149,7 @@ func (b *batch) load(root *folder, queue []categoryAt) error {
return err
}
s := siteIn(c.dir, c.in, c.name)
s.defaultModules = x.defaultModules
*b = append(*b, loaded{site: s, was: x})
if parent := parentOf(s.n); parent != x.entry.Parent {
return fmt.Errorf("%s: the index entry for %s %s, and its table %s", x.src.ManifestPath(), s.path, namesParent(x.entry.Parent), namesParent(parent))
@@ -178,19 +170,6 @@ func (b batch) run(root *folder, sites []categorySite) error {
return categoryPipeline(sites, root.children).run()
}
// withDefaultModules adds to err, where a reference of a category names nothing, the modules
// the category's source reads by default, or that it names none.
func withDefaultModules(err error, of func(category string) []string) error {
var u unresolvedRead
if !errors.As(err, &u) {
return err
}
if modules := of(u.category); len(modules) > 0 {
return fmt.Errorf("%w; the module holding %s reads by default %s", err, u.category, strings.Join(modules, ", "))
}
return fmt.Errorf("%w, and the module holding %s names no module it reads by default", err, u.category)
}
func (b batch) putBack() {
for _, l := range b {
l.site.in.putUnloaded(l.site.name, l.was)
+1 -1
View File
@@ -90,7 +90,7 @@ func loadInline(root *folder, n node, label string, typedByGo bool) error {
if err := refuseFolderRefs(nodes); err != nil {
return err
}
if err := loadReached(root, nil, referenced(root, nil, nodes), nil); err != nil {
if err := loadReached(root, nil, referenced(root, nil, nodes)); err != nil {
return loadError{err}
}
return pipeline{
+18 -17
View File
@@ -3,26 +3,18 @@ package fejkdata
import (
"errors"
"fmt"
"strings"
)
// templateSite is a template to resolve, with its folder and its category's path, both empty for an
// templateSite is a template to resolve, with its folder and its category, both empty for an
// inline template, and eachNode's label for it.
type templateSite struct {
t *template
folder []string
label string
category string
category *categorySite
}
// unresolvedRead is a reference of a category's template naming nothing.
type unresolvedRead struct {
category string
err error
}
func (u unresolvedRead) Error() string { return u.err.Error() }
func (u unresolvedRead) Unwrap() error { return u.err }
// resolveCategoryTemplates resolves every template of the categories, once all data is merged, so a
// reference sees the override-resolved tree.
func resolveCategoryTemplates(sites []categorySite, root map[string]node) error {
@@ -30,7 +22,7 @@ func resolveCategoryTemplates(sites []categorySite, root map[string]node) error
for _, s := range sites {
if err := eachNode(s.n, s.path, func(label string, n node) error {
if t, isTemplate := n.(*template); isTemplate {
ts = append(ts, templateSite{t: t, folder: s.dir, label: label, category: s.path})
ts = append(ts, templateSite{t: t, folder: s.dir, label: label, category: &s})
}
return nil
}); err != nil {
@@ -66,12 +58,8 @@ type resolvedNames struct {
func resolveTemplates(ts []templateSite, root map[string]node) error {
for _, s := range ts {
refs, err := s.t.resolveRefs(s.folder, s.label, root)
var ne noEntry
if err != nil && s.category != "" && errors.As(err, &ne) {
return unresolvedRead{category: s.category, err: err}
}
if err != nil {
return err
return s.withDefaultModules(err)
}
s.t.refs = refs
}
@@ -102,3 +90,16 @@ func resolveTemplates(ts []templateSite, root map[string]node) error {
}
return nil
}
// withDefaultModules adds to err, where a reference of a category names nothing, the modules
// the category's source reads by default, or that it names none.
func (s templateSite) withDefaultModules(err error) error {
var ne noEntry
if s.category == nil || !errors.As(err, &ne) {
return err
}
if modules := s.category.defaultModules; len(modules) > 0 {
return fmt.Errorf("%w; the module holding %s reads by default %s", err, s.category.path, strings.Join(modules, ", "))
}
return fmt.Errorf("%w, and the module holding %s names no module it reads by default", err, s.category.path)
}