Answer architecture round 4: New keeps every module the loaded manifests read, and the note on a failed load names them all from one function beside noDataNote, so no category carries its source's modules

This commit is contained in:
2026-10-11 11:49:18 +02:00
committed by Lillem4n
parent 6e2555aee6
commit 165b67e843
9 changed files with 48 additions and 22 deletions
+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
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
reference to a path nothing provides fails, naming those modules or saying there are
none.
reference to a path nothing provides fails to load, naming every module the loaded
manifests read, or saying there are none.
- 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,
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
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
`github.com/larvit/fejkdata/data/geo/SE`. A reference to a path nothing provides fails,
naming those modules, or saying the manifest names none.
`github.com/larvit/fejkdata/data/geo/SE`. Data whose reference names a path nothing
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`,
`ip`, `last-name`, `person`, `phone`, `price`, `sentence`, `sex`, `time`, `url`,
+20 -10
View File
@@ -1,6 +1,7 @@
package fejkdata
import (
"slices"
"strings"
"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
// carries an index leaves its categories unloaded; every other source loads here, with
// the unloaded categories it needs.
func loadSources(sources []datafiles.Source) (folder, error) {
// the unloaded categories it needs. It also returns, sorted, every module the sources'
// 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{}}
var modules []string
for _, src := range sources {
if err := addSource(&root, src); err != nil {
return folder{}, err
m, err := src.Manifest()
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 {
m, err := src.Manifest()
if err != nil {
return err
}
func addSource(root *folder, src datafiles.Source, m datafiles.Manifest) error {
if m.Index != nil {
placeIndex(root, src, m.Index)
return nil
+5 -3
View File
@@ -31,6 +31,8 @@ type Generator struct {
drawState *drawstate.State
root folder // the categories as the node a path walks from, owned here so a walk allocates none
structs map[reflect.Type]structResult
// defaultModules is every module the loaded sources' manifests read by default.
defaultModules []string
}
type config struct {
@@ -107,15 +109,15 @@ func New(opts ...Option) (*Generator, error) {
if c.err != nil {
return nil, c.err
}
root, err := loadSources(c.sources)
root, modules, err := loadSources(c.sources)
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()
if err != nil {
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,
+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 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
+1 -1
View File
@@ -37,7 +37,7 @@ func (f *Generator) NewTemplate(input string) (*Template, error) {
f.mu.Lock()
defer f.mu.Unlock()
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
}
+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) }
// 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
// data is loaded.
func (f *Generator) noDataNote(err error) error {
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"fmt"
)
// templateSite is a template to resolve, with its folder and category, both empty for an inline
// template, and eachNode's label for it.
// templateSite is a template to resolve, with its folder, empty for an inline template, and
// eachNode's label for it.
type templateSite struct {
t *template
folder []string
+1 -1
View File
@@ -31,7 +31,7 @@ func (f *Generator) FakeStruct(v any) error {
defer f.mu.Unlock()
shape, err := f.structShapeOf(p.Elem().Type())
if err != nil {
return fmt.Errorf("fejkdata: %w", err)
return fmt.Errorf("fejkdata: %w", withDefaultModules(err, f.defaultModules))
}
shape.fill(f.drawState, p.Elem())
return nil