Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33

Open
lilleman wants to merge 966 commits from worktree-todo-relative-dates-exact-counts into main
5 changed files with 66 additions and 57 deletions
Showing only changes of commit e8b7d95559 - Show all commits
+5 -10
View File
@@ -8,7 +8,7 @@ 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: 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) { func loadSources(sources []datafiles.Source) (folder, error) {
root := folder{children: map[string]node{}} root := folder{children: map[string]node{}}
for _, src := range sources { for _, src := range sources {
@@ -25,16 +25,13 @@ func addSource(root *folder, src datafiles.Source) error {
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) {
s.in.setReads(s.name, m.Reads)
}
mergeFolder(root, g) mergeFolder(root, g)
return nil return nil
} }
@@ -73,7 +70,6 @@ func mergeFolder(dst, src *folder) {
} }
} }
dst.put(k, v) 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. // categorySite is a loaded category, where it sits and its own dot path.
type categorySite struct { type categorySite struct {
categoryAt categoryAt
n node n node
path string path string
reads []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.
@@ -107,7 +102,7 @@ func categorySites(root *folder) []categorySite {
} }
func siteIn(dir []string, in *folder, name string) 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) } func categoryPath(dir []string, name string) string { return join(strings.Join(dir, "."), name) }
+1 -1
View File
@@ -48,7 +48,7 @@ func TestPackageImports(t *testing.T) {
"internal/rows": {"internal/drawstate", "internal/grammar", "internal/invariant"}, "internal/rows": {"internal/drawstate", "internal/grammar", "internal/invariant"},
} }
testsAlso := map[string][]string{ 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"}, "cmd/fejkdata": {"internal/grammar"},
"data/en_US": {"."}, "data/en_US": {"."},
"data/geo/SE": {"."}, "data/geo/SE": {"."},
+4 -11
View File
@@ -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) { func TestReadRowsReadsOnlyAFileBesideTheCategory(t *testing.T) {
src := Source{fsys: fstest.MapFS{ src := Source{fsys: fstest.MapFS{
"t.json": {Data: []byte(`{"rows":"t.tsv"}`)}, "t.json": {Data: []byte(`{"rows":"t.tsv"}`)},
@@ -138,10 +131,10 @@ func TestWalkRefusesADiskPathThatIsNoDirectory(t *testing.T) {
func TestLoadHandsOneCategoryAndItsRows(t *testing.T) { func TestLoadHandsOneCategoryAndItsRows(t *testing.T) {
src := Source{fsys: fstest.MapFS{ src := Source{fsys: fstest.MapFS{
"data/sub/t.json": {Data: []byte(`{"rows":"t.tsv"}`)}, "sub/t.json": {Data: []byte(`{"rows":"t.tsv"}`)},
"data/sub/t.tsv": {Data: []byte("key\nA\n")}, "sub/t.tsv": {Data: []byte("key\nA\n")},
"data/sub/u.json": {Data: []byte(`{`)}, "sub/u.json": {Data: []byte(`{`)},
}, base: "data"} }}
var got handed var got handed
var rows string var rows string
err := src.Load([]string{"sub"}, "t", func(c Category) error { err := src.Load([]string{"sub"}, "t", func(c Category) error {
-13
View File
@@ -24,8 +24,6 @@ type node interface{ isNode() }
type folder struct { type folder struct {
children map[string]node children map[string]node
unloaded map[string]*indexed 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() {} func (*folder) isNode() {}
@@ -35,17 +33,6 @@ func (g *folder) put(name string, n node) {
delete(g.unloaded, name) 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) { func (g *folder) putUnloaded(name string, x *indexed) {
delete(g.children, name) delete(g.children, name)
if g.unloaded == nil { if g.unloaded == nil {
+56 -22
View File
@@ -8,7 +8,6 @@ import (
"io/fs" "io/fs"
"maps" "maps"
"os" "os"
"path"
"path/filepath" "path/filepath"
"reflect" "reflect"
"slices" "slices"
@@ -19,6 +18,10 @@ import (
"testing/fstest" "testing/fstest"
"github.com/larvit/fejkdata/data" "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/data/sv_SE"
"github.com/larvit/fejkdata/internal/datafiles" "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, "/", ".")+".") return strings.HasPrefix(path, strings.ReplaceAll(m.dir, "/", ".")+".")
} }
// shippedModules is every module data.Modules lists, each folder found by descending its // shippedModules is every module data.Modules lists, each beside its folder under data/.
// tree while a folder holds one folder and nothing else.
func shippedModules(t testing.TB) []shippedModule { func shippedModules(t testing.TB) []shippedModule {
t.Helper() t.Helper()
var out []shippedModule out := []shippedModule{{en_US.FS, "en_US"}, {SE.FS, "geo/SE"}, {US.FS, "geo/US"}, {misc.FS, "misc"}, {sv_SE.FS, "sv_SE"}}
for _, fsys := range data.Modules() { var listed []fs.FS
dir := "." for _, m := range out {
for { listed = append(listed, m.fsys)
entries, err := fs.ReadDir(fsys, dir) }
if err != nil { if !slices.Equal(listed, data.Modules()) {
t.Fatal(err) t.Fatal("data.Modules() lists other modules than shippedModules does")
}
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})
} }
return out return out
} }
@@ -180,7 +174,7 @@ func shippedIndex(t *testing.T) map[string]datafiles.IndexEntry {
t.Helper() t.Helper()
index := map[string]datafiles.IndexEntry{} index := map[string]datafiles.IndexEntry{}
for _, m := range shippedModules(t) { for _, m := range shippedModules(t) {
manifest, err := datafiles.FS(m.fsys, "").Manifest() manifest, err := datafiles.FS(m.fsys).Manifest()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -191,7 +185,7 @@ func shippedIndex(t *testing.T) map[string]datafiles.IndexEntry {
func TestEachShippedModuleNamesWhatItReads(t *testing.T) { func TestEachShippedModuleNamesWhatItReads(t *testing.T) {
for _, m := range shippedModules(t) { for _, m := range shippedModules(t) {
manifest, err := datafiles.FS(m.fsys, "").Manifest() manifest, err := datafiles.FS(m.fsys).Manifest()
if err != nil { if err != nil {
t.Fatal(err) 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) { func TestAReadNoModuleProvidesNamesTheReadersModules(t *testing.T) {
f, err := New(WithDataFS(sv_SE.FS)) f, err := New(WithDataFS(sv_SE.FS))
if err != nil { 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) { func TestNewLoadsNoShippedCategory(t *testing.T) {
f, err := New(withShipped()) f, err := New(withShipped())
if err != nil { 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": ["", "x", "x"]}}}`: {`paths item 3 repeats "x"`},
`{"index": {"a": {"paths": [""], "parent": "b"}}}`: {`parent "b" names no entry`}, `{"index": {"a": {"paths": [""], "parent": "b"}}}`: {`parent "b" names no entry`},
`{"index": {"a": {"paths": [""], "reads": ["b"]}}}`: {`unknown key "reads"`}, `{"index": {"a": {"paths": [""], "reads": ["b"]}}}`: {`unknown key "reads"`},
`{"reads": "b"}`: {"reads must be a list, not a string"}, `{"index": {}, "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": ["", 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)}})) _, err := New(WithDataFS(fstest.MapFS{".fejkdata.json": {Data: []byte(manifest)}}))
for _, w := range want { for _, w := range want {