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
7 changed files with 62 additions and 27 deletions
Showing only changes of commit 49736bd2b6 - Show all commits
+15 -10
View File
@@ -22,21 +22,26 @@ func grammarLoneReference(text string) (string, bool) {
func TestLoneReferenceAgreesWithTheGrammar(t *testing.T) { func TestLoneReferenceAgreesWithTheGrammar(t *testing.T) {
for _, text := range []string{ for _, text := range []string{
"{/sv_SE.person}", " {/x}",
"x",
"{.x}",
"{/.x}",
"{//x}",
"{/a...b}",
"{/a..b}",
"{/a..}",
"{/a.[x]}",
"{/a.}",
"{/a|/b}",
"{/a}{/b}",
"{/misc.currency[US Dollar (Next day)]}", "{/misc.currency[US Dollar (Next day)]}",
"{/sv_SE.person}",
"{/t[a as b].c}", "{/t[a as b].c}",
"{/t[f(x)]}", "{/t[f(x)]}",
"{/a}{/b}", "{/x as p}",
"{/x(1)}",
"{/x}}", "{/x}}",
"{/}", "{/}",
"{/.x}",
" {/x}",
"{/a|/b}",
"{/x as p}",
"{//x}",
"{/x(1)}",
"{.x}",
"x",
} { } {
path, lone := loneReference(text) path, lone := loneReference(text)
want, wantLone := grammarLoneReference(text) want, wantLone := grammarLoneReference(text)
+2 -2
View File
@@ -495,8 +495,8 @@ func TestRunTemplateMisuse(t *testing.T) {
"42": "number", "42": "number",
"42\n": "number", "42\n": "number",
"true\n": "boolean", "true\n": "boolean",
"null\n": "only null", "null\n": `write ""`,
" null ": "only null", " null ": `write ""`,
"{//sv_SE.person}": "write {/sv_SE.person}", "{//sv_SE.person}": "write {/sv_SE.person}",
} { } {
code, out, errb := runOut(stdin, "--seed", "1") code, out, errb := runOut(stdin, "--seed", "1")
+3 -1
View File
@@ -30,7 +30,9 @@ func TestDatatypeSitsOnlyInAColumn(t *testing.T) {
`{"format":"{int(1,9)}","datatype":"integer"}`: "datatype only types a record column", `{"format":"{int(1,9)}","datatype":"integer"}`: "datatype only types a record column",
`[{"format":"1","datatype":"integer"},"x"]`: "datatype only types a record column", `[{"format":"1","datatype":"integer"},"x"]`: "datatype only types a record column",
`{"format":"{p}","p":{"format":"{n}","n":{"format":"1","datatype":"integer"}}}`: "datatype only types a record column", `{"format":"{p}","p":{"format":"{n}","n":{"format":"1","datatype":"integer"}}}`: "datatype only types a record column",
`{"format":"{n}","repeat":2,"n":{"format":"1","datatype":"integer"}}`: "datatype only types a record column", `{"format":"{n}","repeat":2,"n":{"format":"1","datatype":"integer"}}`: `so it has no effect here; drop "datatype"`,
`null`: `so write ""`,
`{"format":"{p}","p":{"format":"{x}","x":[null,"a"]}}`: `so write ""`,
} { } {
if _, err := resolved(t, src); err == nil || !strings.Contains(err.Error(), want) { if _, err := resolved(t, src); err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("compile(%s) = %v, want an error containing %q", src, err, want) t.Errorf("compile(%s) = %v, want an error containing %q", src, err, want)
+5 -5
View File
@@ -226,23 +226,23 @@ func TestNewErrors(t *testing.T) {
}{ }{
"separator without repeat": { "separator without repeat": {
map[string]string{"a": `{"format":"{x}","x":"1","separator":","}`}, map[string]string{"a": `{"format":"{x}","x":"1","separator":","}`},
"has no effect without a repeat above 1", `drop "separator"`,
}, },
"separator with an explicit repeat of 1": { "separator with an explicit repeat of 1": {
map[string]string{"a": `{"format":"{x}","x":"1","separator":","}`}, map[string]string{"a": `{"format":"{x}","x":"1","separator":","}`},
"has no effect without a repeat above 1", `drop "separator"`,
}, },
"weight outside a choice": { "weight outside a choice": {
map[string]string{"a": `{"format":"x","weight":5}`}, map[string]string{"a": `{"format":"x","weight":5}`},
"weight only skews a choice's items", `drop "weight"`,
}, },
"non-numeric weight outside a choice": { "non-numeric weight outside a choice": {
map[string]string{"a": `{"format":"x","weight":"bad"}`}, map[string]string{"a": `{"format":"x","weight":"bad"}`},
"weight only skews a choice's items", "takes a number, not a string",
}, },
"weight used as a field": { "weight used as a field": {
map[string]string{"a": `{"format":"{name} {weight}kg","name":"Anvil","weight":["7"]}`}, map[string]string{"a": `{"format":"{name} {weight}kg","name":"Anvil","weight":["7"]}`},
"can never be a field", `a field cannot be named "weight"`,
}, },
"an option name used as a token": { "an option name used as a token": {
map[string]string{"a": `"{weight}"`}, map[string]string{"a": `"{weight}"`},
+6
View File
@@ -192,6 +192,12 @@ func TestNameErrors(t *testing.T) {
`no field "zz"`}, `no field "zz"`},
{"read by nothing", map[string]string{"word": `["a","b"]`, "card": `"{/word as w}"`}, {"read by nothing", map[string]string{"word": `["a","b"]`, "card": `"{/word as w}"`},
`nothing reads name "w"`}, `nothing reads name "w"`},
{"one arm read twice in an alternation", map[string]string{"cat": `{"format":"{w|w}","w":["a","b"]}`, "card": `"{/cat as n}{n} {n.w}"`},
""},
{"a field and a path into it in one alternation", map[string]string{"cat": `{"format":"{w|w.x}","w":{"format":"{x}","x":["a","b"]}}`, "card": `"{/cat as n}{n} {n.w}"`},
""},
{"one field read in two alternations", map[string]string{"cat": `{"format":"{w|v}{w|v}","w":["a","b"],"v":["c","d"]}`, "card": `"{/cat as n}{n} {n.w}"`},
`renders field "w" twice`},
{"read in a repeat", map[string]string{"nm.json": `{"format":"{first}","rows":"nm.tsv"}`, "nm.tsv": "first\tlast\nAda\tByron\nBo\tEk\n", "person": `{"format":"{first} {last}","first":"{/nm.first}","last":"{/nm.last}"}`, "card": `{"format":"{/person as p}{each}","each":{"format":"{p.first}","repeat":2}}`}, {"read in a repeat", map[string]string{"nm.json": `{"format":"{first}","rows":"nm.tsv"}`, "nm.tsv": "first\tlast\nAda\tByron\nBo\tEk\n", "person": `{"format":"{first} {last}","first":"{/nm.first}","last":"{/nm.last}"}`, "card": `{"format":"{/person as p}{each}","each":{"format":"{p.first}","repeat":2}}`},
""}, ""},
{"a misspelt name", map[string]string{"word": `["a","b"]`, "card": `"{/word as p}{p}{p}{pp}"`}, {"a misspelt name", map[string]string{"word": `["a","b"]`, "card": `"{/word as p}{p}{p}{pp}"`},
+17 -5
View File
@@ -29,8 +29,18 @@ func TestOneItemChoiceIsItsItem(t *testing.T) {
if r, isTemplate := compiled(t, `[{"format":"","d":"x"}]`).(*template); !isTemplate || !r.isRecord { if r, isTemplate := compiled(t, `[{"format":"","d":"x"}]`).(*template); !isTemplate || !r.isRecord {
t.Error("a one-item choice of a record is no record, want the record it holds") t.Error("a one-item choice of a record is no record, want the record it holds")
} }
if _, err := resolved(t, `[{"format":"a","weight":2}]`); err == nil || !strings.Contains(err.Error(), "no effect here") { }
t.Errorf("a weight on a one-item choice's item = %v, want it refused as doing nothing", err)
func TestAWeightWithNothingToDrawAgainstIsRejected(t *testing.T) {
for _, src := range []string{
`{"format":"a","weight":2}`,
`{"format":"{x}","x":{"format":"a","weight":2}}`,
`[{"format":"a","weight":2}]`,
} {
_, err := resolved(t, src)
if err == nil || !strings.Contains(err.Error(), `drop "weight"`) || strings.Contains(err.Error(), "can never be a field") || strings.Contains(err.Error(), "only skews a choice's items") {
t.Errorf("compile(%s) = %v, want the weight refused as drawn against nothing, naming the drop", src, err)
}
} }
} }
@@ -53,7 +63,7 @@ func TestARepeatedItemCountsAsWritten(t *testing.T) {
func TestASeparatorWithoutARepeatIsRejected(t *testing.T) { func TestASeparatorWithoutARepeatIsRejected(t *testing.T) {
for src, want := range map[string]string{ for src, want := range map[string]string{
`{"format":"{x}","x":"v","separator":","}`: "separator", `{"format":"{x}","x":"v","separator":","}`: `drop "separator"`,
} { } {
if _, err := resolved(t, src); err == nil || !strings.Contains(err.Error(), want) { if _, err := resolved(t, src); err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("compile(%s) = %v, want an error mentioning %s", src, err, want) t.Errorf("compile(%s) = %v, want an error mentioning %s", src, err, want)
@@ -69,8 +79,10 @@ func TestADefaultWrittenOutIsTheDefault(t *testing.T) {
`{"format":"{x}","x":["a","b"],"repeat":1}`: `{"format":"{x}","x":["a","b"]}`, `{"format":"{x}","x":["a","b"],"repeat":1}`: `{"format":"{x}","x":["a","b"]}`,
`{"format":"{x}","x":["a","b"],"repeat":2,"separator":""}`: `{"format":"{x}","x":["a","b"],"repeat":2}`, `{"format":"{x}","x":["a","b"],"repeat":2,"separator":""}`: `{"format":"{x}","x":["a","b"],"repeat":2}`,
`{"format":"","n":{"format":"{x}","x":["1","2"],"datatype":"string"}}`: `{"format":"","n":{"format":"{x}","x":["1","2"]}}`, `{"format":"","n":{"format":"{x}","x":["1","2"],"datatype":"string"}}`: `{"format":"","n":{"format":"{x}","x":["1","2"]}}`,
`null`: `""`, `{"format":"a","weight":1}`: `"a"`,
`{"format":"{p}","p":{"format":"{x}","x":[null,"a"]}}`: `{"format":"{p}","p":{"format":"{x}","x":["","a"]}}`, `[{"format":"a","weight":1}]`: `"a"`,
`{"format":"{x}","x":{"format":"a","weight":1}}`: `{"format":"{x}","x":"a"}`,
`{"format":"{x}","x":["a","b"],"separator":""}`: `{"format":"{x}","x":["a","b"]}`,
} { } {
sameRenders(t, long, short) sameRenders(t, long, short)
} }
+14 -4
View File
@@ -243,6 +243,16 @@ func TestFakeStructReadsAPathInEachSpelling(t *testing.T) {
} }
} }
func TestFakeStructTakesTheDatatypeItsGoTypeSets(t *testing.T) {
v := struct {
N int `fake:"{\"format\":\"{int(1,9)}\",\"datatype\":\"integer\"}"`
S string `fake:"{\"format\":\"x\",\"datatype\":\"string\"}"`
}{}
if err := structData(t).FakeStruct(&v); err != nil || v.N < 1 || v.N > 9 || v.S != "x" {
t.Errorf("FakeStruct = %+v, %v, want each tag's datatype taken as its Go type's", v, err)
}
}
func TestFakeStructErrors(t *testing.T) { func TestFakeStructErrors(t *testing.T) {
f := structData(t) f := structData(t)
for _, c := range []struct { for _, c := range []struct {
@@ -267,11 +277,11 @@ func TestFakeStructErrors(t *testing.T) {
A structPlace `fake:"place"` A structPlace `fake:"place"`
}{}, ".A: a struct field fills from the tags on its own fields"}, }{}, ".A: a struct field fills from the tags on its own fields"},
{&struct { {&struct {
A int `fake:"{\"format\":\"{int(1,9)}\",\"datatype\":\"integer\"}"` A int `fake:"{\"format\":\"{int(1,9)}\",\"datatype\":\"number\"}"`
}{}, `its Go type int sets the datatype; drop "datatype"`}, }{}, `its Go type int sets the datatype integer; drop "datatype"`},
{&struct { {&struct {
A string `fake:"{\"format\":\"x\",\"datatype\":\"string\"}"` A string `fake:"{\"format\":\"1\",\"datatype\":\"integer\"}"`
}{}, `its Go type string sets the datatype; drop "datatype"`}, }{}, `its Go type string sets the datatype string; drop "datatype"`},
{&struct { {&struct {
A int `fake:"[null,\"{int(1,9)}\"]"` A int `fake:"[null,\"{int(1,9)}\"]"`
}{}, "can draw null, which int cannot hold; make it *int"}, }{}, "can draw null, which int cannot hold; make it *int"},