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
Showing only changes of commit 3750770735 - Show all commits
+44 -17
View File
@@ -44,9 +44,6 @@ type shippedModule struct {
namespace string namespace string
} }
// importPath is the Go package the module's FS is exported from.
func (m shippedModule) importPath() string { return "github.com/larvit/fejkdata/data/" + m.dir }
// holds reports whether the category at path is the module's. // holds reports whether the category at path is the module's.
func (m shippedModule) holds(path string) bool { return strings.HasPrefix(path, m.namespace+".") } func (m shippedModule) holds(path string) bool { return strings.HasPrefix(path, m.namespace+".") }
@@ -68,10 +65,14 @@ func (m shippedModule) whole(t testing.TB) fs.FS {
return out return out
} }
// shippedModules is every module data.Modules lists, each beside its folder under data/. // shippedModules is every module data.Modules lists, each beside its folder under data/: its
// namespace in lower case without separators.
func shippedModules(t testing.TB) []shippedModule { func shippedModules(t testing.TB) []shippedModule {
t.Helper() t.Helper()
out := []shippedModule{{enus.FS, "enus", "en_US"}, {geose.FS, "geose", "geo.SE"}, {geous.FS, "geous", "geo.US"}, {misc.FS, "misc", "misc"}, {svse.FS, "svse", "sv_SE"}} out := []shippedModule{{fsys: enus.FS, namespace: "en_US"}, {fsys: geose.FS, namespace: "geo.SE"}, {fsys: geous.FS, namespace: "geo.US"}, {fsys: misc.FS, namespace: "misc"}, {fsys: svse.FS, namespace: "sv_SE"}}
for i := range out {
out[i].dir = strings.ToLower(strings.NewReplacer(".", "", "_", "").Replace(out[i].namespace))
}
var listed []fs.FS var listed []fs.FS
for _, m := range out { for _, m := range out {
listed = append(listed, m.fsys) listed = append(listed, m.fsys)
@@ -171,7 +172,7 @@ func shippedManifests(whole *Generator, modules []shippedModule) ([][]byte, erro
return out, nil return out, nil
} }
// readsOf is every module module i reads, directly or through another, by import path. // readsOf is the namespace of every module module i reads, directly or through another.
func readsOf(i int, direct [][]int, modules []shippedModule) []string { func readsOf(i int, direct [][]int, modules []shippedModule) []string {
seen := map[int]bool{i: true} seen := map[int]bool{i: true}
queue := slices.Clone(direct[i]) queue := slices.Clone(direct[i])
@@ -182,7 +183,7 @@ func readsOf(i int, direct [][]int, modules []shippedModule) []string {
continue continue
} }
seen[j] = true seen[j] = true
out = append(out, modules[j].importPath()) out = append(out, modules[j].namespace)
queue = append(queue, direct[j]...) queue = append(queue, direct[j]...)
} }
sort.Strings(out) sort.Strings(out)
@@ -211,7 +212,7 @@ func TestEachShippedModuleRendersBesideTheModulesItReads(t *testing.T) {
} }
set := []fs.FS{m.fsys} set := []fs.FS{m.fsys}
for _, read := range manifest.Reads { for _, read := range manifest.Reads {
i := slices.IndexFunc(modules, func(r shippedModule) bool { return r.importPath() == read }) i := slices.IndexFunc(modules, func(r shippedModule) bool { return r.namespace == read })
if i < 0 { if i < 0 {
t.Fatalf("%s reads %s, which no shipped module is", m.dir, read) t.Fatalf("%s reads %s, which no shipped module is", m.dir, read)
} }
@@ -234,7 +235,7 @@ func TestAReadNoModuleProvidesNamesTheReadersModules(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("New(WithDataFS(svse.FS)) = %v", err) t.Fatalf("New(WithDataFS(svse.FS)) = %v", err)
} }
if _, err := f.Fake("sv_SE.address"); err == nil || !strings.Contains(err.Error(), "is a module missing? The loaded modules read github.com/larvit/fejkdata/data/geose") { if _, err := f.Fake("sv_SE.address"); err == nil || !strings.Contains(err.Error(), "is a module missing? The loaded modules read geo.SE, which nothing loaded provides") {
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{
@@ -253,23 +254,23 @@ func TestAReadNoModuleProvidesNamesTheReadersModules(t *testing.T) {
func TestADataPathManifestNamesWhatItReads(t *testing.T) { func TestADataPathManifestNamesWhatItReads(t *testing.T) {
dir := writeFiles(t, map[string]string{ dir := writeFiles(t, map[string]string{
".fejkdata.json": `{"reads": ["example.com/places"]}`, ".fejkdata.json": `{"reads": ["places"]}`,
"x.json": `"{/place}"`, "x.json": `"{/place}"`,
}) })
if _, err := New(WithDataPath(dir)); err == nil || !strings.Contains(err.Error(), "is a module missing? The loaded modules read example.com/places") { if _, err := New(WithDataPath(dir)); err == nil || !strings.Contains(err.Error(), "is a module missing? The loaded modules read places, which nothing loaded provides") {
t.Errorf("New = %v, want the module x reads named", err) t.Errorf("New = %v, want the module x reads named", err)
} }
} }
func TestAnIndexedModuleNamesWhatItReads(t *testing.T) { func TestAnIndexedModuleNamesWhatItReads(t *testing.T) {
f, err := New(WithDataFS(fstest.MapFS{ f, err := New(WithDataFS(fstest.MapFS{
".fejkdata.json": {Data: []byte(`{"index": {"x": {"paths": [""]}}, "reads": ["example.com/places"]}`)}, ".fejkdata.json": {Data: []byte(`{"index": {"x": {"paths": [""]}}, "reads": ["places"]}`)},
"x.json": {Data: []byte(`"{/place}"`)}, "x.json": {Data: []byte(`"{/place}"`)},
})) }))
if err != nil { if err != nil {
t.Fatalf("New = %v", err) t.Fatalf("New = %v", err)
} }
const note = "is a module missing? The loaded modules read example.com/places" const note = "is a module missing? The loaded modules read places, which nothing loaded provides"
for name, reach := range map[string]func() error{ for name, reach := range map[string]func() error{
"Fake": func() error { _, err := f.Fake("x"); return err }, "Fake": func() error { _, err := f.Fake("x"); return err },
"FakeTemplate": func() error { _, err := f.FakeTemplate("{/x}"); return err }, "FakeTemplate": func() error { _, err := f.FakeTemplate("{/x}"); return err },
@@ -288,23 +289,49 @@ func TestAnIndexedModuleNamesWhatItReads(t *testing.T) {
} }
} }
func TestATypoUnderLoadedModulesNamesNoModule(t *testing.T) {
for name, opts := range map[string][]Option{
"every shipped module": {withShipped()},
"a stand-in providing what a module reads": {WithDataFS(svse.FS, fstest.MapFS{"geo/SE/address.json": {Data: []byte(`"x"`)}})},
} {
_, err := New(append(opts, WithDataPath(writeData(t, map[string]string{"x": `"{/sv_SE.persn}"`})))...)
if err == nil || !strings.HasSuffix(err.Error(), `no entry "persn", and no loaded manifest names a module to add`) {
t.Errorf("%s: New = %v, want the typo reported with no module to add", name, err)
}
}
}
func TestAMountedModuleNamesItsFilesByItsNamespace(t *testing.T) {
_, err := New(WithDataFS(fstest.MapFS{
".fejkdata.json": {Data: []byte(`{"namespace": "my.data"}`)},
"word.json": {Data: []byte(`{`)},
}))
if err == nil || !strings.Contains(err.Error(), "my/data/word.json") {
t.Errorf("New = %v, want the broken file named under its namespace", err)
}
}
func TestANamespaceMountsAModuleUnderIt(t *testing.T) { func TestANamespaceMountsAModuleUnderIt(t *testing.T) {
for name, opt := range map[string]Option{ for name, opt := range map[string]Option{
"a data path": WithDataPath(writeFiles(t, map[string]string{ "a data path": WithDataPath(writeFiles(t, map[string]string{
".fejkdata.json": `{"namespace": "my.data"}`, ".fejkdata.json": `{"namespace": "my.data"}`,
"word.json": `"hej"`, "word.json": `"hej"`,
"sub/word.json": `"hej"`,
})), })),
"an indexed source": WithDataFS(fstest.MapFS{ "an indexed source": WithDataFS(fstest.MapFS{
".fejkdata.json": {Data: []byte(`{"index": {"my.data.word": {"paths": [""]}}, "namespace": "my.data"}`)}, ".fejkdata.json": {Data: []byte(`{"index": {"my.data.word": {"paths": [""]}, "my.data.sub.word": {"paths": [""]}}, "namespace": "my.data"}`)},
"word.json": {Data: []byte(`"hej"`)}, "word.json": {Data: []byte(`"hej"`)},
"sub/word.json": {Data: []byte(`"hej"`)},
}), }),
} { } {
f, err := New(opt) f, err := New(opt)
if err != nil { if err != nil {
t.Fatalf("New(%s) = %v", name, err) t.Fatalf("New(%s) = %v", name, err)
} }
if got := fake(t, f, "my.data.word"); got != "hej" { for _, p := range []string{"my.data.word", "my.data.sub.word"} {
t.Errorf("%s: my.data.word = %q, want hej", name, got) if got := fake(t, f, p); got != "hej" {
t.Errorf("%s: %s = %q, want hej", name, p, got)
}
} }
} }
} }
@@ -479,7 +506,7 @@ func TestABrokenManifestFailsNewNamingEveryMistake(t *testing.T) {
`{"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"}, `{"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"}, `{"reads": ["", 3]}`: {"reads item 1:", "reads item 2 must be a string, not a number"},
`{"reads": ["a", "a"]}`: {`reads item 2 repeats "a"`}, `{"reads": ["a", "a"]}`: {`reads item 2 repeats "a"`},
`{"namespace": 5}`: {"namespace must be a string, not a number"}, `{"namespace": 5}`: {"namespace must be a string, not a number"},
`{"namespace": "a..b"}`: {`namespace "a..b"`}, `{"namespace": "a..b"}`: {`namespace "a..b"`},