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
9 changed files with 48 additions and 22 deletions
Showing only changes of commit 165b67e843 - Show all commits
+2 -2
View File
@@ -131,8 +131,8 @@ replacement, and each removed path, column or flag.
and the CLI carries them all. `New` loads the modules `WithDataFS(modules...)` names and and the CLI carries them all. `New` loads the modules `WithDataFS(modules...)` names and
the folders `WithDataPath` names, and fails, naming `WithDataFS`, where no such option the folders `WithDataPath` names, and fails, naming `WithDataFS`, where no such option
is given. A manifest's `reads` lists the modules its source reads by default, and a is given. A manifest's `reads` lists the modules its source reads by default, and a
reference to a path nothing provides fails, naming those modules or saying there are reference to a path nothing provides fails to load, naming every module the loaded
none. manifests read, or saying there are none.
- A data source whose root holds a `.fejkdata.json` manifest with an `index` loads each - A data source whose root holds a `.fejkdata.json` manifest with an `index` loads each
category on the first call reaching it, as each shipped module does. Data failing to load, category on the first call reaching it, as each shipped module does. Data failing to load,
in `New` or on that first call, gives an error matching `ErrLoad`, and the CLI exits 1. in `New` or on that first call, gives an error matching `ErrLoad`, and the CLI exits 1.
+3 -2
View File
@@ -195,8 +195,9 @@ A manifest's `reads` lists, by any name its author chooses, every module the sou
references read unless another source provides the same paths, directly or through references read unless another source provides the same paths, directly or through
another module; a manifest may carry it without an index. Nothing loads these modules: another module; a manifest may carry it without an index. Nothing loads these modules:
pass them yourself. A shipped module names a Go package: `sv_SE`'s manifest reads pass them yourself. A shipped module names a Go package: `sv_SE`'s manifest reads
`github.com/larvit/fejkdata/data/geo/SE`. A reference to a path nothing provides fails, `github.com/larvit/fejkdata/data/geo/SE`. Data whose reference names a path nothing
naming those modules, or saying the manifest names none. provides fails to load, naming every module the loaded manifests read, or saying they
name none.
Each locale carries `address`, `color`, `company`, `date`, `email`, `first-name`, Each locale carries `address`, `color`, `company`, `date`, `email`, `first-name`,
`ip`, `last-name`, `person`, `phone`, `price`, `sentence`, `sex`, `time`, `url`, `ip`, `last-name`, `person`, `phone`, `price`, `sentence`, `sex`, `time`, `url`,
+20 -10
View File
@@ -1,6 +1,7 @@
package fejkdata package fejkdata
import ( import (
"slices"
"strings" "strings"
"github.com/larvit/fejkdata/internal/datafiles" "github.com/larvit/fejkdata/internal/datafiles"
@@ -8,22 +9,31 @@ 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. It also returns, sorted, every module the sources'
func loadSources(sources []datafiles.Source) (folder, error) { // manifests read by default.
// 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, []string, error) {
root := folder{children: map[string]node{}} root := folder{children: map[string]node{}}
var modules []string
for _, src := range sources { for _, src := range sources {
if err := addSource(&root, src); err != nil { m, err := src.Manifest()
return folder{}, err if err != nil {
return folder{}, nil, err
}
if err := addSource(&root, src, m); err != nil {
return folder{}, nil, err
}
for _, module := range m.Reads {
if !slices.Contains(modules, module) {
modules = append(modules, module)
}
} }
} }
return root, loadReached(&root, categorySites(&root), nil) slices.Sort(modules)
return root, modules, loadReached(&root, categorySites(&root), nil)
} }
func addSource(root *folder, src datafiles.Source) error { func addSource(root *folder, src datafiles.Source, m datafiles.Manifest) error {
m, err := src.Manifest()
if err != nil {
return err
}
if m.Index != nil { if m.Index != nil {
placeIndex(root, src, m.Index) placeIndex(root, src, m.Index)
return nil return nil
+5 -3
View File
@@ -31,6 +31,8 @@ type Generator struct {
drawState *drawstate.State drawState *drawstate.State
root folder // the categories as the node a path walks from, owned here so a walk allocates none root folder // the categories as the node a path walks from, owned here so a walk allocates none
structs map[reflect.Type]structResult structs map[reflect.Type]structResult
// defaultModules is every module the loaded sources' manifests read by default.
defaultModules []string
} }
type config struct { type config struct {
@@ -107,15 +109,15 @@ func New(opts ...Option) (*Generator, error) {
if c.err != nil { if c.err != nil {
return nil, c.err return nil, c.err
} }
root, err := loadSources(c.sources) root, modules, err := loadSources(c.sources)
if err != nil { if err != nil {
return nil, fmt.Errorf("fejkdata: %w", loadError{err}) return nil, fmt.Errorf("fejkdata: %w", withDefaultModules(loadError{err}, modules))
} }
seed, err := c.drawSeed() seed, err := c.drawSeed()
if err != nil { if err != nil {
return nil, fmt.Errorf("fejkdata: %w", err) return nil, fmt.Errorf("fejkdata: %w", err)
} }
return &Generator{drawState: drawstate.New(seed), root: root}, nil return &Generator{drawState: drawstate.New(seed), root: root, defaultModules: modules}, nil
} }
// List returns the sorted dotted paths Fake renders: each category and every field, // List returns the sorted dotted paths Fake renders: each category and every field,
+1 -1
View File
@@ -59,7 +59,7 @@ func (f *Generator) loadCallerPath(path string) (string, []string, error) {
} }
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}); 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", withDefaultModules(loadError{err}, f.defaultModules))
} }
} }
return path, segs, nil return path, segs, nil
+1 -1
View File
@@ -37,7 +37,7 @@ func (f *Generator) NewTemplate(input string) (*Template, error) {
f.mu.Lock() f.mu.Lock()
defer f.mu.Unlock() defer f.mu.Unlock()
if err := loadInline(&f.root, n, "template", false); err != nil { if err := loadInline(&f.root, n, "template", false); err != nil {
return nil, f.noDataNote(fmt.Errorf("fejkdata: %w", err)) return nil, f.noDataNote(fmt.Errorf("fejkdata: %w", withDefaultModules(err, f.defaultModules)))
} }
return &Template{g: f, n: n}, nil return &Template{g: f, n: n}, nil
} }
+13
View File
@@ -121,6 +121,19 @@ type noEntry struct{ seg string }
func (e noEntry) Error() string { return fmt.Sprintf("no entry %q", e.seg) } func (e noEntry) Error() string { return fmt.Sprintf("no entry %q", e.seg) }
// withDefaultModules adds to err, where data fails to load because a segment names nothing, the
// modules the loaded sources' manifests read by default, or that they name none.
func withDefaultModules(err error, modules []string) error {
var ne noEntry
if !errors.Is(err, ErrLoad) || !errors.As(err, &ne) {
return err
}
if len(modules) == 0 {
return fmt.Errorf("%w, and no loaded module names a module it reads by default", err)
}
return fmt.Errorf("%w; the loaded modules read by default %s", err, strings.Join(modules, ", "))
}
// noDataNote adds to err, where a segment names nothing and f holds no data at all, that no // noDataNote adds to err, where a segment names nothing and f holds no data at all, that no
// data is loaded. // data is loaded.
func (f *Generator) noDataNote(err error) error { func (f *Generator) noDataNote(err error) error {
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"fmt" "fmt"
) )
// templateSite is a template to resolve, with its folder and category, both empty for an inline // templateSite is a template to resolve, with its folder, empty for an inline template, and
// template, and eachNode's label for it. // eachNode's label for it.
type templateSite struct { type templateSite struct {
t *template t *template
folder []string folder []string
+1 -1
View File
@@ -31,7 +31,7 @@ func (f *Generator) FakeStruct(v any) error {
defer f.mu.Unlock() defer f.mu.Unlock()
shape, err := f.structShapeOf(p.Elem().Type()) shape, err := f.structShapeOf(p.Elem().Type())
if err != nil { if err != nil {
return fmt.Errorf("fejkdata: %w", err) return fmt.Errorf("fejkdata: %w", withDefaultModules(err, f.defaultModules))
} }
shape.fill(f.drawState, p.Elem()) shape.fill(f.drawState, p.Elem())
return nil return nil