Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33
+2
-2
@@ -298,8 +298,8 @@ not agree on one among themselves; each must only hold that type.
|
|||||||
|
|
||||||
The fields an embedded struct promotes are the struct's own — `e.First`, as
|
The fields an embedded struct promotes are the struct's own — `e.First`, as
|
||||||
`encoding/json` and SQL mappers read them — so they are columns of its record and share
|
`encoding/json` and SQL mappers read them — so they are columns of its record and share
|
||||||
its names: one tag may bind `{/person as p}{p.first}` and another read `{p.sex}`, while a
|
its names: one tag may bind `{/person as p}{p.first}` and another read `{p.sex}` (goal 4.1),
|
||||||
path tag draws afresh as `{/path}` does (goal 4.1); a tagged field that another field hides is refused, not dropped. A named
|
while a path tag draws afresh as `{/path}` does (goal 5.2); a tagged field that another field hides is refused, not dropped. A named
|
||||||
struct field is another entity and a record of its own. `fake:"-"` leaves a struct
|
struct field is another entity and a record of its own. `fake:"-"` leaves a struct
|
||||||
field, embedded or named, unfilled, so no name may be `-`; a pointer back to a struct
|
field, embedded or named, unfilled, so no name may be `-`; a pointer back to a struct
|
||||||
already being filled is left alone, since filling it would never end. `New` cannot see a
|
already being filled is left alone, since filling it would never end. `New` cannot see a
|
||||||
|
|||||||
@@ -174,6 +174,9 @@ func checkNoCycles(s nodeScope) error {
|
|||||||
inScope := map[node]bool{}
|
inScope := map[node]bool{}
|
||||||
_ = s(func(_ string, n node) error {
|
_ = s(func(_ string, n node) error {
|
||||||
inScope[n] = true
|
inScope[n] = true
|
||||||
|
if t, isTable := n.(*table); isTable {
|
||||||
|
inScope[t.rowNode] = true // a selector lands on it, and contained lists it nowhere
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
color := map[node]int{}
|
color := map[node]int{}
|
||||||
|
|||||||
+3
-1
@@ -58,7 +58,9 @@ func rendersInside(nodes []node, t *template) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// checkUses refuses a binding read once at a spot the bound spelling can stand: that spelling
|
// checkUses refuses a binding read once at a spot the bound spelling can stand: that spelling
|
||||||
// draws the same way without the name. A field stands only beside its binding.
|
// draws the same way without the name. A field stands only beside its binding. Where the spelling
|
||||||
|
// would be a CLI argument or tag of one reference alone, that entry point's own refusal then
|
||||||
|
// names the bare path.
|
||||||
func (b *nameBinding) checkUses() error {
|
func (b *nameBinding) checkUses() error {
|
||||||
r := b.uses[0]
|
r := b.uses[0]
|
||||||
if len(b.uses) > 1 || r.nested || b.bindsField() && r.in != b.binder {
|
if len(b.uses) > 1 || r.nested || b.bindsField() && r.in != b.binder {
|
||||||
|
|||||||
@@ -339,3 +339,14 @@ func TestSlashAfterAReferenceSigilIsRejected(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestACycleThroughASelectedRowIsRefused(t *testing.T) {
|
||||||
|
_, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, map[string]string{
|
||||||
|
"x.json": `{"format":"{v} {/y}","rows":"x.tsv","key":"code"}`,
|
||||||
|
"x.tsv": "code\tv\n1\ta\n2\tb\n",
|
||||||
|
"y.json": `"{/x[1]}"`,
|
||||||
|
})))
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "cycle") {
|
||||||
|
t.Fatalf("New = %v, want the cycle through x[1] refused", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user