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:
@@ -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
|
// 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) {
|
func loadSources(sources []datafiles.Source) (folder, error) {
|
||||||
root := folder{children: map[string]node{}}
|
root := folder{children: map[string]node{}}
|
||||||
defaultModules := map[string][]string{}
|
walked := map[string][]string{}
|
||||||
for _, src := range sources {
|
for _, src := range sources {
|
||||||
if err := addSource(&root, src, defaultModules); err != nil {
|
if err := addSource(&root, src, walked); err != nil {
|
||||||
return folder{}, err
|
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
|
// addSource places src's categories in root, and sets in walked, for each category it loads
|
||||||
// it loads, the modules src reads by default.
|
// here, the modules src reads by default.
|
||||||
func addSource(root *folder, src datafiles.Source, defaultModules map[string][]string) error {
|
func addSource(root *folder, src datafiles.Source, walked map[string][]string) error {
|
||||||
m, err := src.Manifest()
|
m, err := src.Manifest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -36,7 +40,7 @@ func addSource(root *folder, src datafiles.Source, defaultModules map[string][]s
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, s := range categorySites(g) {
|
for _, s := range categorySites(g) {
|
||||||
defaultModules[s.path] = m.Reads
|
walked[s.path] = m.Reads
|
||||||
}
|
}
|
||||||
mergeFolder(root, g)
|
mergeFolder(root, g)
|
||||||
return nil
|
return nil
|
||||||
@@ -83,11 +87,13 @@ func mergeFolder(dst, src *folder) {
|
|||||||
// single inline node.
|
// single inline node.
|
||||||
type nodeSet func(fn func(label string, n node) error) error
|
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 {
|
type categorySite struct {
|
||||||
categoryAt
|
categoryAt
|
||||||
n node
|
n node
|
||||||
path string
|
path string
|
||||||
|
defaultModules []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// categorySites lists every category under root, folders and names in sorted order.
|
// categorySites lists every category under root, folders and names in sorted order.
|
||||||
|
|||||||
+3
-2
@@ -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.
|
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,
|
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`
|
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
|
checks no `reads`: a check by module name fails a stand-in providing the same paths (goal
|
||||||
(goal 9.2).
|
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.
|
`--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
|
## 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
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ func (f *Generator) loadCallerPath(path string) (string, []string, error) {
|
|||||||
return "", nil, fmt.Errorf("fejkdata: %w", err)
|
return "", nil, fmt.Errorf("fejkdata: %w", err)
|
||||||
}
|
}
|
||||||
if c, ok := categoryOn(&f.root, segs); ok && c.unloaded() {
|
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})
|
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
|
// 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
|
// 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
|
// error it puts back what it loaded, so the next call fails the same way.
|
||||||
// holds, by path, the modules the source of each of sites reads by default.
|
func loadReached(root *folder, sites []categorySite, wanted []categoryAt) error {
|
||||||
func loadReached(root *folder, sites []categorySite, wanted []categoryAt, defaultModules map[string][]string) error {
|
|
||||||
queue := wanted
|
queue := wanted
|
||||||
for _, s := range sites {
|
for _, s := range sites {
|
||||||
queue = append(queue, needs(root, s)...)
|
queue = append(queue, needs(root, s)...)
|
||||||
@@ -120,14 +119,6 @@ func loadReached(root *folder, sites []categorySite, wanted []categoryAt, defaul
|
|||||||
err = b.run(root, sites)
|
err = b.run(root, sites)
|
||||||
}
|
}
|
||||||
if err != nil {
|
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()
|
b.putBack()
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@@ -158,6 +149,7 @@ func (b *batch) load(root *folder, queue []categoryAt) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s := siteIn(c.dir, c.in, c.name)
|
s := siteIn(c.dir, c.in, c.name)
|
||||||
|
s.defaultModules = x.defaultModules
|
||||||
*b = append(*b, loaded{site: s, was: x})
|
*b = append(*b, loaded{site: s, was: x})
|
||||||
if parent := parentOf(s.n); parent != x.entry.Parent {
|
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))
|
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()
|
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() {
|
func (b batch) putBack() {
|
||||||
for _, l := range b {
|
for _, l := range b {
|
||||||
l.site.in.putUnloaded(l.site.name, l.was)
|
l.site.in.putUnloaded(l.site.name, l.was)
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ func loadInline(root *folder, n node, label string, typedByGo bool) error {
|
|||||||
if err := refuseFolderRefs(nodes); err != nil {
|
if err := refuseFolderRefs(nodes); err != nil {
|
||||||
return err
|
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 loadError{err}
|
||||||
}
|
}
|
||||||
return pipeline{
|
return pipeline{
|
||||||
|
|||||||
+18
-17
@@ -3,26 +3,18 @@ package fejkdata
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"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.
|
// inline template, and eachNode's label for it.
|
||||||
type templateSite struct {
|
type templateSite struct {
|
||||||
t *template
|
t *template
|
||||||
folder []string
|
folder []string
|
||||||
label 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
|
// resolveCategoryTemplates resolves every template of the categories, once all data is merged, so a
|
||||||
// reference sees the override-resolved tree.
|
// reference sees the override-resolved tree.
|
||||||
func resolveCategoryTemplates(sites []categorySite, root map[string]node) error {
|
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 {
|
for _, s := range sites {
|
||||||
if err := eachNode(s.n, s.path, func(label string, n node) error {
|
if err := eachNode(s.n, s.path, func(label string, n node) error {
|
||||||
if t, isTemplate := n.(*template); isTemplate {
|
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
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
@@ -66,12 +58,8 @@ type resolvedNames struct {
|
|||||||
func resolveTemplates(ts []templateSite, root map[string]node) error {
|
func resolveTemplates(ts []templateSite, root map[string]node) error {
|
||||||
for _, s := range ts {
|
for _, s := range ts {
|
||||||
refs, err := s.t.resolveRefs(s.folder, s.label, root)
|
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 {
|
if err != nil {
|
||||||
return err
|
return s.withDefaultModules(err)
|
||||||
}
|
}
|
||||||
s.t.refs = refs
|
s.t.refs = refs
|
||||||
}
|
}
|
||||||
@@ -102,3 +90,16 @@ func resolveTemplates(ts []templateSite, root map[string]node) error {
|
|||||||
}
|
}
|
||||||
return nil
|
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)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user