From 3da487f8eab8944b8402032e5f570c8b38950b30 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sat, 10 Oct 2026 13:20:26 +0200 Subject: [PATCH] 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 --- data.go | 26 ++++++++++++++++---------- docs/decisions.md | 5 +++-- index.go | 29 ++++------------------------- inline.go | 2 +- resolve.go | 35 ++++++++++++++++++----------------- 5 files changed, 42 insertions(+), 55 deletions(-) diff --git a/data.go b/data.go index c542424..86ec6bf 100644 --- a/data.go +++ b/data.go @@ -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 + n node + path string + defaultModules []string } // categorySites lists every category under root, folders and names in sorted order. diff --git a/docs/decisions.md b/docs/decisions.md index 2ecf559..c9b5fa6 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -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 diff --git a/index.go b/index.go index 25ce958..5d59045 100644 --- a/index.go +++ b/index.go @@ -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) diff --git a/inline.go b/inline.go index 660b765..a9e15b3 100644 --- a/inline.go +++ b/inline.go @@ -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{ diff --git a/resolve.go b/resolve.go index 01f8295..7b90db4 100644 --- a/resolve.go +++ b/resolve.go @@ -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) +}