Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
func loadSources(sources []datafiles.Source) (folder, error) {
|
||||
root := folder{children: map[string]node{}}
|
||||
for _, src := range sources {
|
||||
@@ -25,16 +25,13 @@ func addSource(root *folder, src datafiles.Source) error {
|
||||
return err
|
||||
}
|
||||
if m.Index != nil {
|
||||
placeIndex(root, src, m)
|
||||
placeIndex(root, src, m.Index)
|
||||
return nil
|
||||
}
|
||||
g := &folder{children: map[string]node{}}
|
||||
if err := src.Walk(compileInto(func(dir []string) *folder { return madeFolder(g, dir) })); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range categorySites(g) {
|
||||
s.in.setReads(s.name, m.Reads)
|
||||
}
|
||||
mergeFolder(root, g)
|
||||
return nil
|
||||
}
|
||||
@@ -73,7 +70,6 @@ func mergeFolder(dst, src *folder) {
|
||||
}
|
||||
}
|
||||
dst.put(k, v)
|
||||
dst.setReads(k, src.reads[k])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,9 +80,8 @@ type nodeSet func(fn func(label string, n node) error) error
|
||||
// categorySite is a loaded category, where it sits and its own dot path.
|
||||
type categorySite struct {
|
||||
categoryAt
|
||||
n node
|
||||
path string
|
||||
reads []string
|
||||
n node
|
||||
path string
|
||||
}
|
||||
|
||||
// categorySites lists every category under root, folders and names in sorted order.
|
||||
@@ -107,7 +102,7 @@ func categorySites(root *folder) []categorySite {
|
||||
}
|
||||
|
||||
func siteIn(dir []string, in *folder, name string) categorySite {
|
||||
return categorySite{categoryAt: categoryAt{dir: dir, in: in, name: name}, n: in.children[name], path: categoryPath(dir, name), reads: in.reads[name]}
|
||||
return categorySite{categoryAt: categoryAt{dir: dir, in: in, name: name}, n: in.children[name], path: categoryPath(dir, name)}
|
||||
}
|
||||
|
||||
func categoryPath(dir []string, name string) string { return join(strings.Join(dir, "."), name) }
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ func TestPackageImports(t *testing.T) {
|
||||
"internal/rows": {"internal/drawstate", "internal/grammar", "internal/invariant"},
|
||||
}
|
||||
testsAlso := map[string][]string{
|
||||
".": {"data", "data/sv_SE"},
|
||||
".": {"data", "data/en_US", "data/geo/SE", "data/geo/US", "data/misc", "data/sv_SE"},
|
||||
"cmd/fejkdata": {"internal/grammar"},
|
||||
"data/en_US": {"."},
|
||||
"data/geo/SE": {"."},
|
||||
|
||||
@@ -49,13 +49,6 @@ func TestWalkHandsEachCategoryInOrder(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWalkStartsAtTheBaseDir(t *testing.T) {
|
||||
got, err := walk(t, Source{fsys: fstest.MapFS{"data/a.json": {Data: []byte(`"x"`)}, "b.json": {Data: []byte(`"y"`)}}, base: "data"})
|
||||
if err != nil || len(got) != 1 || got[0].name != "a" || got[0].dir != nil {
|
||||
t.Errorf("Walk = %#v, %v, want a alone at the base", got, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadRowsReadsOnlyAFileBesideTheCategory(t *testing.T) {
|
||||
src := Source{fsys: fstest.MapFS{
|
||||
"t.json": {Data: []byte(`{"rows":"t.tsv"}`)},
|
||||
@@ -138,10 +131,10 @@ func TestWalkRefusesADiskPathThatIsNoDirectory(t *testing.T) {
|
||||
|
||||
func TestLoadHandsOneCategoryAndItsRows(t *testing.T) {
|
||||
src := Source{fsys: fstest.MapFS{
|
||||
"data/sub/t.json": {Data: []byte(`{"rows":"t.tsv"}`)},
|
||||
"data/sub/t.tsv": {Data: []byte("key\nA\n")},
|
||||
"data/sub/u.json": {Data: []byte(`{`)},
|
||||
}, base: "data"}
|
||||
"sub/t.json": {Data: []byte(`{"rows":"t.tsv"}`)},
|
||||
"sub/t.tsv": {Data: []byte("key\nA\n")},
|
||||
"sub/u.json": {Data: []byte(`{`)},
|
||||
}}
|
||||
var got handed
|
||||
var rows string
|
||||
err := src.Load([]string{"sub"}, "t", func(c Category) error {
|
||||
|
||||
@@ -24,8 +24,6 @@ type node interface{ isNode() }
|
||||
type folder struct {
|
||||
children map[string]node
|
||||
unloaded map[string]*indexed
|
||||
// reads is, for each category, the modules its source's manifest names it reading by default.
|
||||
reads map[string][]string
|
||||
}
|
||||
|
||||
func (*folder) isNode() {}
|
||||
@@ -35,17 +33,6 @@ func (g *folder) put(name string, n node) {
|
||||
delete(g.unloaded, name)
|
||||
}
|
||||
|
||||
func (g *folder) setReads(name string, reads []string) {
|
||||
if len(reads) == 0 {
|
||||
delete(g.reads, name)
|
||||
return
|
||||
}
|
||||
if g.reads == nil {
|
||||
g.reads = map[string][]string{}
|
||||
}
|
||||
g.reads[name] = reads
|
||||
}
|
||||
|
||||
func (g *folder) putUnloaded(name string, x *indexed) {
|
||||
delete(g.children, name)
|
||||
if g.unloaded == nil {
|
||||
|
||||
+56
-22
@@ -8,7 +8,6 @@ import (
|
||||
"io/fs"
|
||||
"maps"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"slices"
|
||||
@@ -19,6 +18,10 @@ import (
|
||||
"testing/fstest"
|
||||
|
||||
"github.com/larvit/fejkdata/data"
|
||||
"github.com/larvit/fejkdata/data/en_US"
|
||||
"github.com/larvit/fejkdata/data/geo/SE"
|
||||
"github.com/larvit/fejkdata/data/geo/US"
|
||||
"github.com/larvit/fejkdata/data/misc"
|
||||
"github.com/larvit/fejkdata/data/sv_SE"
|
||||
"github.com/larvit/fejkdata/internal/datafiles"
|
||||
)
|
||||
@@ -47,25 +50,16 @@ func (m shippedModule) holds(path string) bool {
|
||||
return strings.HasPrefix(path, strings.ReplaceAll(m.dir, "/", ".")+".")
|
||||
}
|
||||
|
||||
// shippedModules is every module data.Modules lists, each folder found by descending its
|
||||
// tree while a folder holds one folder and nothing else.
|
||||
// shippedModules is every module data.Modules lists, each beside its folder under data/.
|
||||
func shippedModules(t testing.TB) []shippedModule {
|
||||
t.Helper()
|
||||
var out []shippedModule
|
||||
for _, fsys := range data.Modules() {
|
||||
dir := "."
|
||||
for {
|
||||
entries, err := fs.ReadDir(fsys, dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
entries = slices.DeleteFunc(entries, func(e fs.DirEntry) bool { return strings.HasPrefix(e.Name(), ".") })
|
||||
if len(entries) != 1 || !entries[0].IsDir() {
|
||||
break
|
||||
}
|
||||
dir = path.Join(dir, entries[0].Name())
|
||||
}
|
||||
out = append(out, shippedModule{fsys: fsys, dir: dir})
|
||||
out := []shippedModule{{en_US.FS, "en_US"}, {SE.FS, "geo/SE"}, {US.FS, "geo/US"}, {misc.FS, "misc"}, {sv_SE.FS, "sv_SE"}}
|
||||
var listed []fs.FS
|
||||
for _, m := range out {
|
||||
listed = append(listed, m.fsys)
|
||||
}
|
||||
if !slices.Equal(listed, data.Modules()) {
|
||||
t.Fatal("data.Modules() lists other modules than shippedModules does")
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -180,7 +174,7 @@ func shippedIndex(t *testing.T) map[string]datafiles.IndexEntry {
|
||||
t.Helper()
|
||||
index := map[string]datafiles.IndexEntry{}
|
||||
for _, m := range shippedModules(t) {
|
||||
manifest, err := datafiles.FS(m.fsys, "").Manifest()
|
||||
manifest, err := datafiles.FS(m.fsys).Manifest()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -191,7 +185,7 @@ func shippedIndex(t *testing.T) map[string]datafiles.IndexEntry {
|
||||
|
||||
func TestEachShippedModuleNamesWhatItReads(t *testing.T) {
|
||||
for _, m := range shippedModules(t) {
|
||||
manifest, err := datafiles.FS(m.fsys, "").Manifest()
|
||||
manifest, err := datafiles.FS(m.fsys).Manifest()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -204,6 +198,33 @@ func TestEachShippedModuleNamesWhatItReads(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEachShippedModuleRendersBesideTheModulesItReads(t *testing.T) {
|
||||
modules := shippedModules(t)
|
||||
for _, m := range modules {
|
||||
manifest, err := datafiles.FS(m.fsys).Manifest()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
set := []fs.FS{m.fsys}
|
||||
for _, read := range manifest.Reads {
|
||||
i := slices.IndexFunc(modules, func(r shippedModule) bool { return r.importPath() == read })
|
||||
if i < 0 {
|
||||
t.Fatalf("%s reads %s, which no shipped module is", m.dir, read)
|
||||
}
|
||||
set = append(set, modules[i].fsys)
|
||||
}
|
||||
f, err := New(WithDataFS(set...))
|
||||
if err != nil {
|
||||
t.Fatalf("New(%s with what it reads) = %v", m.dir, err)
|
||||
}
|
||||
for _, p := range sortedNames(manifest.Index) {
|
||||
if _, err := f.Fake(p); err != nil {
|
||||
t.Errorf("%s with what it reads: Fake(%q) = %v", m.dir, p, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAReadNoModuleProvidesNamesTheReadersModules(t *testing.T) {
|
||||
f, err := New(WithDataFS(sv_SE.FS))
|
||||
if err != nil {
|
||||
@@ -236,6 +257,19 @@ func TestADataPathManifestNamesWhatItReads(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnIndexedModuleNamesWhatItReads(t *testing.T) {
|
||||
f, err := New(WithDataFS(fstest.MapFS{
|
||||
".fejkdata.json": {Data: []byte(`{"index": {"x": {"paths": [""]}}, "reads": ["example.com/places"]}`)},
|
||||
"x.json": {Data: []byte(`"{/place}"`)},
|
||||
}))
|
||||
if err != nil {
|
||||
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") {
|
||||
t.Errorf(`Fake("x") = %v, want the module x reads named`, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewLoadsNoShippedCategory(t *testing.T) {
|
||||
f, err := New(withShipped())
|
||||
if err != nil {
|
||||
@@ -405,8 +439,8 @@ func TestABrokenManifestFailsNewNamingEveryMistake(t *testing.T) {
|
||||
`{"index": {"a": {"paths": ["", "x", "x"]}}}`: {`paths item 3 repeats "x"`},
|
||||
`{"index": {"a": {"paths": [""], "parent": "b"}}}`: {`parent "b" names no entry`},
|
||||
`{"index": {"a": {"paths": [""], "reads": ["b"]}}}`: {`unknown key "reads"`},
|
||||
`{"reads": "b"}`: {"reads must be a list, not a string"},
|
||||
`{"reads": ["", 3]}`: {"reads item 1 is empty", "reads item 2 must be a string, not a number"},
|
||||
`{"index": {}, "reads": "b"}`: {"reads must be a list, not a string"},
|
||||
`{"index": {}, "reads": ["", 3]}`: {"reads item 1 is empty", "reads item 2 must be a string, not a number"},
|
||||
} {
|
||||
_, err := New(WithDataFS(fstest.MapFS{".fejkdata.json": {Data: []byte(manifest)}}))
|
||||
for _, w := range want {
|
||||
|
||||
Reference in New Issue
Block a user