Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33
+3
-3
@@ -28,7 +28,7 @@ func fenceRoots(t *testing.T, f *Generator) ([]fenceRoot, map[string]*table) {
|
||||
t.Helper()
|
||||
var roots []fenceRoot
|
||||
tables := map[string]*table{}
|
||||
_ = walkNodes(f.categories, func(path string, n node) error {
|
||||
_ = walkNodes(f.root.children, func(path string, n node) error {
|
||||
switch n := n.(type) {
|
||||
case *template:
|
||||
roots = append(roots, fenceRoot{path, n})
|
||||
@@ -61,7 +61,7 @@ func TestReplayPairsFindsWhatAWholeReplayFinds(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("New() = %v", err)
|
||||
}
|
||||
_ = walkNodes(shipped.categories, func(path string, n node) error {
|
||||
_ = walkNodes(shipped.root.children, func(path string, n node) error {
|
||||
if tm, ok := n.(*template); ok {
|
||||
roots = append(roots, fenceRoot{path, tm})
|
||||
}
|
||||
@@ -85,7 +85,7 @@ func TestReplayPairsFindsWhatAWholeReplayOfSelectedRowsFinds(t *testing.T) {
|
||||
t.Fatalf("New = %v", err)
|
||||
}
|
||||
var reads []pathRead
|
||||
_ = walkNodes(f.categories, func(_ string, n node) error {
|
||||
_ = walkNodes(f.root.children, func(_ string, n node) error {
|
||||
tb, isTable := n.(*table)
|
||||
if !isTable || tb.keyIndex < 0 || tb.familyRoot().path != "region" {
|
||||
return nil
|
||||
|
||||
+8
-9
@@ -31,12 +31,11 @@ var ErrNoData = errors.New("no data: WithoutShippedData needs at least one WithD
|
||||
// from one goroutine. The compiled tree is immutable after [New], so List, NewTemplate
|
||||
// and NewRecordTemplate read it without a lock; mu guards the rest, which every render takes.
|
||||
type Generator struct {
|
||||
mu sync.Mutex
|
||||
rand *session
|
||||
categories map[string]node
|
||||
root folder // the categories as the node a path walks from, owned here so a walk allocates none
|
||||
records map[node]recordShape
|
||||
structs map[reflect.Type]structResult
|
||||
mu sync.Mutex
|
||||
rand *session
|
||||
root folder // the categories as the node a path walks from, owned here so a walk allocates none
|
||||
records map[node]recordShape
|
||||
structs map[reflect.Type]structResult
|
||||
}
|
||||
|
||||
// session is one generator's mutable render state: the seeded rng plus the {seq()}
|
||||
@@ -112,7 +111,7 @@ func New(opts ...Option) (*Generator, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fejkdata: %w", err)
|
||||
}
|
||||
return &Generator{rand: rng, categories: cats}, nil
|
||||
return &Generator{rand: rng, root: folder{children: cats}}, nil
|
||||
}
|
||||
|
||||
// List returns the sorted dotted paths Fake renders: each category and every field,
|
||||
@@ -120,8 +119,8 @@ func New(opts ...Option) (*Generator, error) {
|
||||
// segment, so a path continues through one only where every variant carries it.
|
||||
func (f *Generator) List() []string {
|
||||
var out []string
|
||||
for _, name := range sortedNames(f.categories) {
|
||||
for _, p := range paths(f.categories[name]) {
|
||||
for _, name := range sortedNames(f.root.children) {
|
||||
for _, p := range paths(f.root.children[name]) {
|
||||
out = append(out, join(name, p))
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -152,9 +152,8 @@ func isFixed(n node) bool {
|
||||
}
|
||||
|
||||
// renders reports whether rendering n can reach anything in want, following the
|
||||
// same edges expand does. seen keeps a node shared by several routes from being
|
||||
// walked twice; checkNoCycles has already proved the graph is a DAG, so the walk
|
||||
// ends.
|
||||
// same edges expand does. seen keeps a node from being walked twice, so the walk
|
||||
// ends across a cycle too.
|
||||
func renders(n node, want, seen map[node]bool) bool {
|
||||
if want[n] {
|
||||
return true
|
||||
|
||||
@@ -32,7 +32,7 @@ func (f *Generator) NewTemplate(input string) (*Template, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fejkdata: %w", err)
|
||||
}
|
||||
if err := inlineBinding(n, "template", f.categories).bind(); err != nil {
|
||||
if err := inlineBinding(n, "template", f.root.children).bind(); err != nil {
|
||||
return nil, fmt.Errorf("fejkdata: %w", err)
|
||||
}
|
||||
return &Template{g: f, n: n}, nil
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
// node is a compiled element of the namespace tree: a folder, choice, null,
|
||||
// template, table, column or row. Compiling JSON into these once (see compile)
|
||||
// means rendering never re-inspects the raw JSON or re-sums weights. A new kind needs a
|
||||
// means rendering never re-inspects the raw JSON. A new kind needs a
|
||||
// case in contained, paths, render and renderEdges; each panics on a kind it does not list.
|
||||
type node interface{ isNode() }
|
||||
|
||||
|
||||
+3
-3
@@ -50,7 +50,7 @@ func TestDeepDottedPath(t *testing.T) {
|
||||
// A 5-segment path descends through alternating object/array nodes; choices
|
||||
// on the path are single-variant, so it resolves deterministically.
|
||||
f := engine(1)
|
||||
f.categories = map[string]node{
|
||||
f.root.children = map[string]node{
|
||||
"deep": compiled(t, `{"format":"{a}","a":{"format":"{b}","b":{"format":"{c}","c":{"format":"{d}","d":"leaf"}}}}`),
|
||||
}
|
||||
if got, err := f.Fake("deep.a.b.c.d"); err != nil || got != "leaf" {
|
||||
@@ -64,7 +64,7 @@ func TestDeepDottedPath(t *testing.T) {
|
||||
|
||||
func TestDescendIntoStringErrors(t *testing.T) {
|
||||
f := engine(1)
|
||||
f.categories = map[string]node{"greeting": compiled(t, `"hej"`)}
|
||||
f.root.children = map[string]node{"greeting": compiled(t, `"hej"`)}
|
||||
if _, err := f.Fake("greeting.extra"); err == nil || !strings.Contains(err.Error(), `no field "extra"`) {
|
||||
t.Fatalf("Fake(greeting.extra) = %v, want a no-field error", err)
|
||||
}
|
||||
@@ -153,7 +153,7 @@ func TestBindingKeyIsNotAPathSegment(t *testing.T) {
|
||||
|
||||
func TestFakePathNavigation(t *testing.T) {
|
||||
f := engine(1)
|
||||
f.categories = map[string]node{
|
||||
f.root.children = map[string]node{
|
||||
"addr": compiled(t, `{"format":"{street}","street":"Main"}`),
|
||||
}
|
||||
if got, err := f.Fake("addr"); err != nil || !strings.Contains(got, "Main") {
|
||||
|
||||
@@ -126,7 +126,7 @@ func (f *Generator) FakeRecord(path string) (*Record, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fejkdata: %w", err)
|
||||
}
|
||||
_, n, tail, err := resolveCategory(f.categories, segments)
|
||||
_, n, tail, err := resolveCategory(f.root.children, segments)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fejkdata: %s: %w", path, err)
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ func (f *Generator) Fake(path string) (string, error) {
|
||||
}
|
||||
var set holdSet
|
||||
sc := renderScope{set: &set}
|
||||
f.root.children = f.categories
|
||||
n, err := descend(f.rand, &f.root, segments, sc)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("fejkdata: %s: %w", path, err)
|
||||
|
||||
+2
-2
@@ -61,8 +61,8 @@ func shippedShape(f *Generator) string {
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, name := range sortedNames(f.categories) {
|
||||
walk(name, f.categories[name])
|
||||
for _, name := range sortedNames(f.root.children) {
|
||||
walk(name, f.root.children[name])
|
||||
}
|
||||
var b strings.Builder
|
||||
for _, p := range f.List() {
|
||||
|
||||
@@ -49,7 +49,7 @@ func (f *Generator) structShapeOf(t reflect.Type) (*structShape, error) {
|
||||
if label == "" {
|
||||
label = "struct"
|
||||
}
|
||||
sc := &structCompile{categories: f.categories, visiting: map[reflect.Type]bool{}, structsLeft: maxStructs}
|
||||
sc := &structCompile{categories: f.root.children, visiting: map[reflect.Type]bool{}, structsLeft: maxStructs}
|
||||
shape, err := sc.compileShape(t, label)
|
||||
if err == nil && shape.empty() {
|
||||
err = fmt.Errorf("%s has no fake tags, so nothing to fill", t)
|
||||
|
||||
+2
-2
@@ -991,7 +991,7 @@ func TestEnteredRowsAgreeWithPinning(t *testing.T) {
|
||||
f := newGenerator(t, writeFiles(t, geo()), WithSeed(1))
|
||||
var tables []*table
|
||||
for _, category := range []string{"region", "municipality", "locality"} {
|
||||
tbl, isTable := f.categories[category].(*table)
|
||||
tbl, isTable := f.root.children[category].(*table)
|
||||
if !isTable {
|
||||
t.Fatalf("%s is not a table", category)
|
||||
}
|
||||
@@ -1063,7 +1063,7 @@ func TestProbeReportsTheTablesADrawPins(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
head := f.categories[segs[0]].(*table)
|
||||
head := f.root.children[segs[0]].(*table)
|
||||
probe := &pathProbe{drawn: map[*table]bool{}}
|
||||
if _, err := probe.walk(head, segs[1:]); err != nil {
|
||||
t.Fatalf("%s: %v", path, err)
|
||||
|
||||
@@ -26,10 +26,6 @@ least modify.
|
||||
|
||||
This round, in order:
|
||||
|
||||
- Correct the false comments: `node` says rendering never re-sums weights, which
|
||||
`builtLookup` does on first draw; `Generator` claims immutable after `New`, while
|
||||
`Fake` sets `root.children` each call, which belongs in `New`; `renders` credits
|
||||
`checkNoCycles`, which an inline binding never runs.
|
||||
- Run the nine-seat panel again and file what it names here.
|
||||
|
||||
### Data
|
||||
|
||||
Reference in New Issue
Block a user