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()
|
t.Helper()
|
||||||
var roots []fenceRoot
|
var roots []fenceRoot
|
||||||
tables := map[string]*table{}
|
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) {
|
switch n := n.(type) {
|
||||||
case *template:
|
case *template:
|
||||||
roots = append(roots, fenceRoot{path, n})
|
roots = append(roots, fenceRoot{path, n})
|
||||||
@@ -61,7 +61,7 @@ func TestReplayPairsFindsWhatAWholeReplayFinds(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("New() = %v", err)
|
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 {
|
if tm, ok := n.(*template); ok {
|
||||||
roots = append(roots, fenceRoot{path, tm})
|
roots = append(roots, fenceRoot{path, tm})
|
||||||
}
|
}
|
||||||
@@ -85,7 +85,7 @@ func TestReplayPairsFindsWhatAWholeReplayOfSelectedRowsFinds(t *testing.T) {
|
|||||||
t.Fatalf("New = %v", err)
|
t.Fatalf("New = %v", err)
|
||||||
}
|
}
|
||||||
var reads []pathRead
|
var reads []pathRead
|
||||||
_ = walkNodes(f.categories, func(_ string, n node) error {
|
_ = walkNodes(f.root.children, func(_ string, n node) error {
|
||||||
tb, isTable := n.(*table)
|
tb, isTable := n.(*table)
|
||||||
if !isTable || tb.keyIndex < 0 || tb.familyRoot().path != "region" {
|
if !isTable || tb.keyIndex < 0 || tb.familyRoot().path != "region" {
|
||||||
return nil
|
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
|
// 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.
|
// and NewRecordTemplate read it without a lock; mu guards the rest, which every render takes.
|
||||||
type Generator struct {
|
type Generator struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
rand *session
|
rand *session
|
||||||
categories map[string]node
|
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
|
records map[node]recordShape
|
||||||
records map[node]recordShape
|
structs map[reflect.Type]structResult
|
||||||
structs map[reflect.Type]structResult
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// session is one generator's mutable render state: the seeded rng plus the {seq()}
|
// 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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("fejkdata: %w", err)
|
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,
|
// 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.
|
// segment, so a path continues through one only where every variant carries it.
|
||||||
func (f *Generator) List() []string {
|
func (f *Generator) List() []string {
|
||||||
var out []string
|
var out []string
|
||||||
for _, name := range sortedNames(f.categories) {
|
for _, name := range sortedNames(f.root.children) {
|
||||||
for _, p := range paths(f.categories[name]) {
|
for _, p := range paths(f.root.children[name]) {
|
||||||
out = append(out, join(name, p))
|
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
|
// 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
|
// same edges expand does. seen keeps a node from being walked twice, so the walk
|
||||||
// walked twice; checkNoCycles has already proved the graph is a DAG, so the walk
|
// ends across a cycle too.
|
||||||
// ends.
|
|
||||||
func renders(n node, want, seen map[node]bool) bool {
|
func renders(n node, want, seen map[node]bool) bool {
|
||||||
if want[n] {
|
if want[n] {
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func (f *Generator) NewTemplate(input string) (*Template, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("fejkdata: %w", err)
|
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 nil, fmt.Errorf("fejkdata: %w", err)
|
||||||
}
|
}
|
||||||
return &Template{g: f, n: n}, nil
|
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,
|
// 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)
|
// 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.
|
// case in contained, paths, render and renderEdges; each panics on a kind it does not list.
|
||||||
type node interface{ isNode() }
|
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
|
// A 5-segment path descends through alternating object/array nodes; choices
|
||||||
// on the path are single-variant, so it resolves deterministically.
|
// on the path are single-variant, so it resolves deterministically.
|
||||||
f := engine(1)
|
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"}}}}`),
|
"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" {
|
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) {
|
func TestDescendIntoStringErrors(t *testing.T) {
|
||||||
f := engine(1)
|
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"`) {
|
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)
|
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) {
|
func TestFakePathNavigation(t *testing.T) {
|
||||||
f := engine(1)
|
f := engine(1)
|
||||||
f.categories = map[string]node{
|
f.root.children = map[string]node{
|
||||||
"addr": compiled(t, `{"format":"{street}","street":"Main"}`),
|
"addr": compiled(t, `{"format":"{street}","street":"Main"}`),
|
||||||
}
|
}
|
||||||
if got, err := f.Fake("addr"); err != nil || !strings.Contains(got, "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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("fejkdata: %w", err)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("fejkdata: %s: %w", path, err)
|
return nil, fmt.Errorf("fejkdata: %s: %w", path, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ func (f *Generator) Fake(path string) (string, error) {
|
|||||||
}
|
}
|
||||||
var set holdSet
|
var set holdSet
|
||||||
sc := renderScope{set: &set}
|
sc := renderScope{set: &set}
|
||||||
f.root.children = f.categories
|
|
||||||
n, err := descend(f.rand, &f.root, segments, sc)
|
n, err := descend(f.rand, &f.root, segments, sc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("fejkdata: %s: %w", path, err)
|
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) {
|
for _, name := range sortedNames(f.root.children) {
|
||||||
walk(name, f.categories[name])
|
walk(name, f.root.children[name])
|
||||||
}
|
}
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
for _, p := range f.List() {
|
for _, p := range f.List() {
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func (f *Generator) structShapeOf(t reflect.Type) (*structShape, error) {
|
|||||||
if label == "" {
|
if label == "" {
|
||||||
label = "struct"
|
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)
|
shape, err := sc.compileShape(t, label)
|
||||||
if err == nil && shape.empty() {
|
if err == nil && shape.empty() {
|
||||||
err = fmt.Errorf("%s has no fake tags, so nothing to fill", t)
|
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))
|
f := newGenerator(t, writeFiles(t, geo()), WithSeed(1))
|
||||||
var tables []*table
|
var tables []*table
|
||||||
for _, category := range []string{"region", "municipality", "locality"} {
|
for _, category := range []string{"region", "municipality", "locality"} {
|
||||||
tbl, isTable := f.categories[category].(*table)
|
tbl, isTable := f.root.children[category].(*table)
|
||||||
if !isTable {
|
if !isTable {
|
||||||
t.Fatalf("%s is not a table", category)
|
t.Fatalf("%s is not a table", category)
|
||||||
}
|
}
|
||||||
@@ -1063,7 +1063,7 @@ func TestProbeReportsTheTablesADrawPins(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
head := f.categories[segs[0]].(*table)
|
head := f.root.children[segs[0]].(*table)
|
||||||
probe := &pathProbe{drawn: map[*table]bool{}}
|
probe := &pathProbe{drawn: map[*table]bool{}}
|
||||||
if _, err := probe.walk(head, segs[1:]); err != nil {
|
if _, err := probe.walk(head, segs[1:]); err != nil {
|
||||||
t.Fatalf("%s: %v", path, err)
|
t.Fatalf("%s: %v", path, err)
|
||||||
|
|||||||
@@ -26,10 +26,6 @@ least modify.
|
|||||||
|
|
||||||
This round, in order:
|
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.
|
- Run the nine-seat panel again and file what it names here.
|
||||||
|
|
||||||
### Data
|
### Data
|
||||||
|
|||||||
Reference in New Issue
Block a user