Test that a load failing on a read nothing provides names every module the loaded manifests read
This commit is contained in:
@@ -9,40 +9,29 @@ import (
|
|||||||
// loadSources merges sources in order, the last winning a clash. A source whose manifest
|
// loadSources merges sources in order, the last winning a clash. A source whose manifest
|
||||||
// carries an index leaves its categories unloaded; every other source loads here, with
|
// carries an index leaves its categories unloaded; every other source loads here, with
|
||||||
// the unloaded categories it needs.
|
// 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{}}
|
||||||
walked := map[string][]string{}
|
|
||||||
for _, src := range sources {
|
for _, src := range sources {
|
||||||
if err := addSource(&root, src, walked); err != nil {
|
if err := addSource(&root, src); err != nil {
|
||||||
return folder{}, err
|
return folder{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sites := categorySites(&root)
|
return root, loadReached(&root, categorySites(&root), nil)
|
||||||
for i := range sites {
|
|
||||||
sites[i].defaultModules = walked[sites[i].path]
|
|
||||||
}
|
|
||||||
return root, loadReached(&root, sites, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// addSource places src's categories in root. For each category it loads now, it records in
|
func addSource(root *folder, src datafiles.Source) error {
|
||||||
// walked the modules src reads by default.
|
|
||||||
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
|
||||||
}
|
}
|
||||||
if m.Index != nil {
|
if m.Index != nil {
|
||||||
placeIndex(root, src, m)
|
placeIndex(root, src, m.Index)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
g := &folder{children: map[string]node{}}
|
g := &folder{children: map[string]node{}}
|
||||||
if err := src.Walk(compileInto(func(dir []string) *folder { return madeFolder(g, dir) })); err != nil {
|
if err := src.Walk(compileInto(func(dir []string) *folder { return madeFolder(g, dir) })); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, s := range categorySites(g) {
|
|
||||||
walked[s.path] = m.Reads
|
|
||||||
}
|
|
||||||
mergeFolder(root, g)
|
mergeFolder(root, g)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -88,13 +77,11 @@ 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, its own dot path, and the modules its
|
// categorySite is a loaded category, where it sits and its own dot path.
|
||||||
// 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.
|
||||||
|
|||||||
@@ -11,20 +11,18 @@ import (
|
|||||||
"github.com/larvit/fejkdata/internal/grammar"
|
"github.com/larvit/fejkdata/internal/grammar"
|
||||||
)
|
)
|
||||||
|
|
||||||
// indexed is a category an index names and no call has reached yet, the source holding it,
|
// indexed is a category an index names and no call has reached yet, and the source holding it.
|
||||||
// and the modules that source reads by default.
|
|
||||||
type indexed struct {
|
type indexed struct {
|
||||||
entry datafiles.IndexEntry
|
entry datafiles.IndexEntry
|
||||||
src datafiles.Source
|
src datafiles.Source
|
||||||
defaultModules []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// placeIndex sets every category m's index names as unloaded in root, in place of what
|
// placeIndex sets every category the index names as unloaded in root, in place of what
|
||||||
// root holds at its path.
|
// root holds at its path.
|
||||||
func placeIndex(root *folder, src datafiles.Source, m datafiles.Manifest) {
|
func placeIndex(root *folder, src datafiles.Source, index map[string]datafiles.IndexEntry) {
|
||||||
for _, p := range sortedNames(m.Index) {
|
for _, p := range sortedNames(index) {
|
||||||
segs := strings.Split(p, ".")
|
segs := strings.Split(p, ".")
|
||||||
madeFolder(root, segs[:len(segs)-1]).putUnloaded(segs[len(segs)-1], &indexed{entry: m.Index[p], src: src, defaultModules: m.Reads})
|
madeFolder(root, segs[:len(segs)-1]).putUnloaded(segs[len(segs)-1], &indexed{entry: index[p], src: src})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +147,6 @@ 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))
|
||||||
|
|||||||
+1
-1
@@ -206,7 +206,7 @@ func TestNewErrorPathIsCanonical(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"bad reference reached through another reference",
|
"bad reference reached through another reference",
|
||||||
map[string]string{"a": `"{/b}"`, "b": `"{/nope}"`},
|
map[string]string{"a": `"{/b}"`, "b": `"{/nope}"`},
|
||||||
`fejkdata: b: reference {/nope}: no entry "nope", and the module holding b names no module it reads by default`,
|
`fejkdata: b: reference {/nope}: no entry "nope", and no loaded module names a module it reads by default`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
|
|||||||
+7
-23
@@ -1,18 +1,15 @@
|
|||||||
package fejkdata
|
package fejkdata
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// templateSite is a template to resolve, with its folder and its category, both empty for an
|
// templateSite is a template to resolve, with its folder and category, both empty for an inline
|
||||||
// inline template, and eachNode's label for it.
|
// 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 *categorySite
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@@ -22,7 +19,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})
|
ts = append(ts, templateSite{t: t, folder: s.dir, label: label})
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
@@ -59,7 +56,7 @@ 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)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return s.withDefaultModules(err)
|
return err
|
||||||
}
|
}
|
||||||
s.t.refs = refs
|
s.t.refs = refs
|
||||||
}
|
}
|
||||||
@@ -90,16 +87,3 @@ func resolveTemplates(ts []templateSite, root map[string]node) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Where err is a category's reference finding nothing, withDefaultModules adds the modules the
|
|
||||||
// category's source reads by default, or says it reads 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)
|
|
||||||
}
|
|
||||||
|
|||||||
+5
-5
@@ -215,7 +215,7 @@ func TestAReadNoModuleProvidesNamesTheReadersModules(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("New(WithDataFS(sv_SE.FS)) = %v", err)
|
t.Fatalf("New(WithDataFS(sv_SE.FS)) = %v", err)
|
||||||
}
|
}
|
||||||
if _, err := f.Fake("sv_SE.address"); err == nil || !strings.Contains(err.Error(), "the module holding sv_SE.address reads by default github.com/larvit/fejkdata/data/geo/SE") {
|
if _, err := f.Fake("sv_SE.address"); err == nil || !strings.Contains(err.Error(), "the loaded modules read by default github.com/larvit/fejkdata/data/geo/SE") {
|
||||||
t.Errorf(`Fake("sv_SE.address") = %v, want the module it reads named`, err)
|
t.Errorf(`Fake("sv_SE.address") = %v, want the module it reads named`, err)
|
||||||
}
|
}
|
||||||
for name, opt := range map[string]Option{
|
for name, opt := range map[string]Option{
|
||||||
@@ -226,8 +226,8 @@ func TestAReadNoModuleProvidesNamesTheReadersModules(t *testing.T) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
_, err = f.Fake("x")
|
_, err = f.Fake("x")
|
||||||
}
|
}
|
||||||
if err == nil || !strings.Contains(err.Error(), "the module holding x names no module it reads by default") {
|
if err == nil || !strings.Contains(err.Error(), "no loaded module names a module it reads by default") {
|
||||||
t.Errorf("%s: x = %v, want it said that x's module names none", name, err)
|
t.Errorf("%s: x = %v, want it said that no loaded module names one", name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -237,7 +237,7 @@ func TestADataPathManifestNamesWhatItReads(t *testing.T) {
|
|||||||
".fejkdata.json": `{"reads": ["example.com/places"]}`,
|
".fejkdata.json": `{"reads": ["example.com/places"]}`,
|
||||||
"x.json": `"{/place}"`,
|
"x.json": `"{/place}"`,
|
||||||
})
|
})
|
||||||
if _, err := New(WithDataPath(dir)); err == nil || !strings.Contains(err.Error(), "the module holding x reads by default example.com/places") {
|
if _, err := New(WithDataPath(dir)); err == nil || !strings.Contains(err.Error(), "the loaded modules read by default example.com/places") {
|
||||||
t.Errorf("New = %v, want the module x reads named", err)
|
t.Errorf("New = %v, want the module x reads named", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,7 +250,7 @@ func TestAnIndexedModuleNamesWhatItReads(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("New = %v", err)
|
t.Fatalf("New = %v", err)
|
||||||
}
|
}
|
||||||
if _, err := f.Fake("x"); err == nil || !strings.Contains(err.Error(), "the module holding x reads by default example.com/places") {
|
if _, err := f.Fake("x"); err == nil || !strings.Contains(err.Error(), "the loaded modules read by default example.com/places") {
|
||||||
t.Errorf(`Fake("x") = %v, want the module x reads named`, err)
|
t.Errorf(`Fake("x") = %v, want the module x reads named`, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user