Test that a mounted module names a broken file beside its namespace, that a manifest refuses reads, and that each shipped module renders beside the modules its categories reach
This commit is contained in:
+1
-1
@@ -38,7 +38,7 @@ func TestPackageImports(t *testing.T) {
|
|||||||
"data/misc": nil,
|
"data/misc": nil,
|
||||||
"data/svse": nil,
|
"data/svse": nil,
|
||||||
"internal/builtinfunc": {"internal/datatype", "internal/drawstate", "internal/grammar", "internal/invariant", "internal/proven"},
|
"internal/builtinfunc": {"internal/datatype", "internal/drawstate", "internal/grammar", "internal/invariant", "internal/proven"},
|
||||||
"internal/datafiles": {"internal/grammar", "internal/jsonvalue"},
|
"internal/datafiles": {"internal/grammar", "internal/invariant", "internal/jsonvalue"},
|
||||||
"internal/datatype": nil,
|
"internal/datatype": nil,
|
||||||
"internal/drawstate": nil,
|
"internal/drawstate": nil,
|
||||||
"internal/grammar": nil,
|
"internal/grammar": nil,
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ func (f *Generator) loadCallerPath(path string) (string, []string, error) {
|
|||||||
return "", nil, fmt.Errorf("fejkdata: %w", err)
|
return "", nil, fmt.Errorf("fejkdata: %w", err)
|
||||||
}
|
}
|
||||||
if c, ok := categoryOn(&f.root, segs); ok && c.unloaded() {
|
if c, ok := categoryOn(&f.root, segs); ok && c.unloaded() {
|
||||||
if err := loadReached(&f.root, f.manifestReads, nil, []categoryAt{c}); err != nil {
|
if err := loadReached(&f.root, nil, []categoryAt{c}); err != nil {
|
||||||
return "", nil, fmt.Errorf("fejkdata: %w", loadError{err})
|
return "", nil, fmt.Errorf("fejkdata: %w", loadError{err})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,9 +105,8 @@ func needs(root *folder, s categorySite) []categoryAt {
|
|||||||
|
|
||||||
// loadReached loads the unloaded categories among wanted and every unloaded category they
|
// loadReached loads the unloaded categories among wanted and every unloaded category they
|
||||||
// or sites need, then runs sites and all it loaded through a whole load's pipeline. On an
|
// or sites need, then runs sites and all it loaded through a whole load's pipeline. On an
|
||||||
// error it puts back what it loaded, so the next call fails the same way, and notes which
|
// error it puts back what it loaded, so the next call fails the same way.
|
||||||
// namespace of reads, every namespace the loaded manifests read, nothing in root provides.
|
func loadReached(root *folder, sites []categorySite, wanted []categoryAt) error {
|
||||||
func loadReached(root *folder, reads []string, sites []categorySite, wanted []categoryAt) error {
|
|
||||||
queue := wanted
|
queue := wanted
|
||||||
for _, s := range sites {
|
for _, s := range sites {
|
||||||
queue = append(queue, needs(root, s)...)
|
queue = append(queue, needs(root, s)...)
|
||||||
@@ -119,9 +118,8 @@ func loadReached(root *folder, reads []string, sites []categorySite, wanted []ca
|
|||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.putBack()
|
b.putBack()
|
||||||
return withMissingModules(err, root, reads)
|
|
||||||
}
|
}
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// batch is the categories one loadReached loaded, each beside the entry it was loaded from.
|
// batch is the categories one loadReached loaded, each beside the entry it was loaded from.
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func (f *Generator) NewTemplate(input string) (*Template, error) {
|
|||||||
}
|
}
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
defer f.mu.Unlock()
|
||||||
if err := loadInline(&f.root, f.manifestReads, n, "template", false); err != nil {
|
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", err))
|
||||||
}
|
}
|
||||||
return &Template{g: f, n: n}, nil
|
return &Template{g: f, n: n}, nil
|
||||||
@@ -84,14 +84,13 @@ func inputValue(input string) jsonvalue.Value {
|
|||||||
return raw
|
return raw
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadInline loads the unloaded categories n reads, then runs n through the pipeline against root;
|
// loadInline loads the unloaded categories n reads, then runs n through the pipeline against root.
|
||||||
// reads is every namespace the loaded manifests read.
|
func loadInline(root *folder, n node, label string, typedByGo bool) error {
|
||||||
func loadInline(root *folder, reads []string, n node, label string, typedByGo bool) error {
|
|
||||||
nodes := inlineNodes(n, label)
|
nodes := inlineNodes(n, label)
|
||||||
if err := refuseFolderRefs(nodes); err != nil {
|
if err := refuseFolderRefs(nodes); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := loadReached(root, reads, nil, referenced(root, nil, nodes)); err != nil {
|
if err := loadReached(root, nil, referenced(root, nil, nodes)); err != nil {
|
||||||
return loadError{err}
|
return loadError{err}
|
||||||
}
|
}
|
||||||
return pipeline{
|
return pipeline{
|
||||||
|
|||||||
@@ -121,27 +121,6 @@ type noEntry struct{ seg string }
|
|||||||
|
|
||||||
func (e noEntry) Error() string { return fmt.Sprintf("no entry %q", e.seg) }
|
func (e noEntry) Error() string { return fmt.Sprintf("no entry %q", e.seg) }
|
||||||
|
|
||||||
// withMissingModules adds to err, where a path names nothing, each namespace of reads that
|
|
||||||
// nothing in root provides, as the modules a loaded manifest reads and none loaded; or else
|
|
||||||
// that no loaded manifest names a module to add, the mistake being the path's own.
|
|
||||||
func withMissingModules(err error, root *folder, reads []string) error {
|
|
||||||
var ne noEntry
|
|
||||||
if !errors.As(err, &ne) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var missing []string
|
|
||||||
for _, namespace := range reads {
|
|
||||||
segs := strings.Split(namespace, ".")
|
|
||||||
if _, i := folderAt(root, segs); i < len(segs) {
|
|
||||||
missing = append(missing, namespace)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(missing) == 0 {
|
|
||||||
return fmt.Errorf("%w, and no loaded manifest names a module to add", err)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("%w; is a module missing? The loaded modules read %s, which nothing loaded provides", err, strings.Join(missing, ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
// noDataNote adds to err, where a segment names nothing and f holds no data at all, that no
|
// noDataNote adds to err, where a segment names nothing and f holds no data at all, that no
|
||||||
// data is loaded.
|
// data is loaded.
|
||||||
func (f *Generator) noDataNote(err error) error {
|
func (f *Generator) noDataNote(err error) error {
|
||||||
|
|||||||
+1
-1
@@ -206,7 +206,7 @@ func TestNewErrorPathIsCanonical(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"bad reference reached through another reference",
|
"bad reference reached through another reference",
|
||||||
map[string]string{"a": `"{/b}"`, "b": `"{/nope}"`},
|
map[string]string{"a": `"{/b}"`, "b": `"{/nope}"`},
|
||||||
`fejkdata: b: reference {/nope}: no entry "nope", and no loaded manifest names a module to add`,
|
`fejkdata: b: reference {/nope}: no entry "nope"`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
|
|||||||
+18
-92
@@ -1,7 +1,6 @@
|
|||||||
package fejkdata
|
package fejkdata
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -110,7 +109,7 @@ func loadedCategories(f *Generator) []string {
|
|||||||
// REGENERATE=1 rewrites the manifests.
|
// REGENERATE=1 rewrites the manifests.
|
||||||
func TestShippedManifestsAreCurrent(t *testing.T) {
|
func TestShippedManifestsAreCurrent(t *testing.T) {
|
||||||
modules := shippedModules(t)
|
modules := shippedModules(t)
|
||||||
manifests, err := shippedManifests(newShippedWhole(t), modules)
|
manifests, _, err := shippedManifests(newShippedWhole(t), modules)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -133,8 +132,9 @@ func TestShippedManifestsAreCurrent(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// shippedManifests is each module's manifest, read from whole's tree: its index, one category a
|
// shippedManifests is each module's manifest, read from whole's tree: its index, one category a
|
||||||
// line, and every other module it reads, directly or through another.
|
// line, and its namespace; and the namespace of every other module each reads, directly or
|
||||||
func shippedManifests(whole *Generator, modules []shippedModule) ([][]byte, error) {
|
// through another.
|
||||||
|
func shippedManifests(whole *Generator, modules []shippedModule) ([][]byte, [][]string, error) {
|
||||||
owner := func(path string) int {
|
owner := func(path string) int {
|
||||||
return slices.IndexFunc(modules, func(m shippedModule) bool { return m.holds(path) })
|
return slices.IndexFunc(modules, func(m shippedModule) bool { return m.holds(path) })
|
||||||
}
|
}
|
||||||
@@ -143,11 +143,11 @@ func shippedManifests(whole *Generator, modules []shippedModule) ([][]byte, erro
|
|||||||
for _, s := range categorySites(&whole.root) {
|
for _, s := range categorySites(&whole.root) {
|
||||||
i := owner(s.path)
|
i := owner(s.path)
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
return nil, fmt.Errorf("no shipped module holds %s", s.path)
|
return nil, nil, fmt.Errorf("no shipped module holds %s", s.path)
|
||||||
}
|
}
|
||||||
line, err := json.Marshal(indexEntry(s))
|
line, err := json.Marshal(indexEntry(s))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
key, _ := json.Marshal(s.path)
|
key, _ := json.Marshal(s.path)
|
||||||
index[i] = append(index[i], "\n\t\t"+string(key)+": "+string(line))
|
index[i] = append(index[i], "\n\t\t"+string(key)+": "+string(line))
|
||||||
@@ -158,18 +158,13 @@ func shippedManifests(whole *Generator, modules []shippedModule) ([][]byte, erro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
out := make([][]byte, len(modules))
|
out := make([][]byte, len(modules))
|
||||||
|
reads := make([][]string, len(modules))
|
||||||
for i := range modules {
|
for i := range modules {
|
||||||
var b bytes.Buffer
|
|
||||||
namespace, _ := json.Marshal(modules[i].namespace)
|
namespace, _ := json.Marshal(modules[i].namespace)
|
||||||
b.WriteString("{\n\t\"index\": {" + strings.Join(index[i], ",") + "\n\t},\n\t\"namespace\": " + string(namespace))
|
out[i] = []byte("{\n\t\"index\": {" + strings.Join(index[i], ",") + "\n\t},\n\t\"namespace\": " + string(namespace) + "\n}\n")
|
||||||
if reads := readsOf(i, direct, modules); len(reads) > 0 {
|
reads[i] = readsOf(i, direct, modules)
|
||||||
line, _ := json.Marshal(reads)
|
|
||||||
b.WriteString(",\n\t\"reads\": " + string(line))
|
|
||||||
}
|
}
|
||||||
b.WriteString("\n}\n")
|
return out, reads, nil
|
||||||
out[i] = b.Bytes()
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// readsOf is the namespace of every module module i reads, directly or through another.
|
// readsOf is the namespace of every module module i reads, directly or through another.
|
||||||
@@ -205,13 +200,17 @@ func shippedIndex(t *testing.T) map[string]datafiles.IndexEntry {
|
|||||||
|
|
||||||
func TestEachShippedModuleRendersBesideTheModulesItReads(t *testing.T) {
|
func TestEachShippedModuleRendersBesideTheModulesItReads(t *testing.T) {
|
||||||
modules := shippedModules(t)
|
modules := shippedModules(t)
|
||||||
for _, m := range modules {
|
_, reads, err := shippedManifests(newShippedWhole(t), modules)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for i, m := range modules {
|
||||||
manifest, err := datafiles.FS(m.fsys).Manifest()
|
manifest, err := datafiles.FS(m.fsys).Manifest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
set := []fs.FS{m.fsys}
|
set := []fs.FS{m.fsys}
|
||||||
for _, read := range manifest.Reads {
|
for _, read := range reads[i] {
|
||||||
i := slices.IndexFunc(modules, func(r shippedModule) bool { return r.namespace == read })
|
i := slices.IndexFunc(modules, func(r shippedModule) bool { return r.namespace == read })
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
t.Fatalf("%s reads %s, which no shipped module is", m.dir, read)
|
t.Fatalf("%s reads %s, which no shipped module is", m.dir, read)
|
||||||
@@ -230,83 +229,12 @@ func TestEachShippedModuleRendersBesideTheModulesItReads(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAReadNoModuleProvidesNamesTheReadersModules(t *testing.T) {
|
|
||||||
f, err := New(WithDataFS(svse.FS))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("New(WithDataFS(svse.FS)) = %v", err)
|
|
||||||
}
|
|
||||||
if _, err := f.Fake("sv_SE.address"); err == nil || !strings.Contains(err.Error(), "is a module missing? The loaded modules read geo.SE, which nothing loaded provides") {
|
|
||||||
t.Errorf(`Fake("sv_SE.address") = %v, want the module it reads named`, err)
|
|
||||||
}
|
|
||||||
for name, opt := range map[string]Option{
|
|
||||||
"no manifest": WithDataPath(writeData(t, map[string]string{"x": `"{/y}"`})),
|
|
||||||
"no reads in one": WithDataFS(fstest.MapFS{".fejkdata.json": {Data: []byte(`{"index": {"x": {"paths": [""]}}}`)}, "x.json": {Data: []byte(`"{/y}"`)}}),
|
|
||||||
} {
|
|
||||||
f, err := New(opt)
|
|
||||||
if err == nil {
|
|
||||||
_, err = f.Fake("x")
|
|
||||||
}
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "no loaded manifest names a module to add") {
|
|
||||||
t.Errorf("%s: x = %v, want it said that no loaded module names one", name, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestADataPathManifestNamesWhatItReads(t *testing.T) {
|
|
||||||
dir := writeFiles(t, map[string]string{
|
|
||||||
".fejkdata.json": `{"reads": ["places"]}`,
|
|
||||||
"x.json": `"{/place}"`,
|
|
||||||
})
|
|
||||||
if _, err := New(WithDataPath(dir)); err == nil || !strings.Contains(err.Error(), "is a module missing? The loaded modules read places, which nothing loaded provides") {
|
|
||||||
t.Errorf("New = %v, want the module x reads named", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAnIndexedModuleNamesWhatItReads(t *testing.T) {
|
|
||||||
f, err := New(WithDataFS(fstest.MapFS{
|
|
||||||
".fejkdata.json": {Data: []byte(`{"index": {"x": {"paths": [""]}}, "reads": ["places"]}`)},
|
|
||||||
"x.json": {Data: []byte(`"{/place}"`)},
|
|
||||||
}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("New = %v", err)
|
|
||||||
}
|
|
||||||
const note = "is a module missing? The loaded modules read places, which nothing loaded provides"
|
|
||||||
for name, reach := range map[string]func() error{
|
|
||||||
"Fake": func() error { _, err := f.Fake("x"); return err },
|
|
||||||
"FakeTemplate": func() error { _, err := f.FakeTemplate("{/x}"); return err },
|
|
||||||
"FakeStruct": func() error {
|
|
||||||
return f.FakeStruct(&struct {
|
|
||||||
A string `fake:"/x"`
|
|
||||||
}{})
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
if err := reach(); err == nil || strings.Count(err.Error(), note) != 1 {
|
|
||||||
t.Errorf("%s reaching x = %v, want the module x reads named once", name, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, err := f.FakeTemplate("{/nope}"); err == nil || strings.Contains(err.Error(), "module") {
|
|
||||||
t.Errorf("FakeTemplate({/nope}) = %v, want no module named: no load failed", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestATypoUnderLoadedModulesNamesNoModule(t *testing.T) {
|
|
||||||
for name, opts := range map[string][]Option{
|
|
||||||
"every shipped module": {withShipped()},
|
|
||||||
"a stand-in providing what a module reads": {WithDataFS(svse.FS, fstest.MapFS{"geo/SE/address.json": {Data: []byte(`"x"`)}})},
|
|
||||||
} {
|
|
||||||
_, err := New(append(opts, WithDataPath(writeData(t, map[string]string{"x": `"{/sv_SE.persn}"`})))...)
|
|
||||||
if err == nil || !strings.HasSuffix(err.Error(), `no entry "persn", and no loaded manifest names a module to add`) {
|
|
||||||
t.Errorf("%s: New = %v, want the typo reported with no module to add", name, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAMountedModuleNamesItsFilesByItsNamespace(t *testing.T) {
|
func TestAMountedModuleNamesItsFilesByItsNamespace(t *testing.T) {
|
||||||
_, err := New(WithDataFS(fstest.MapFS{
|
_, err := New(WithDataFS(fstest.MapFS{
|
||||||
".fejkdata.json": {Data: []byte(`{"namespace": "my.data"}`)},
|
".fejkdata.json": {Data: []byte(`{"namespace": "my.data"}`)},
|
||||||
"word.json": {Data: []byte(`{`)},
|
"word.json": {Data: []byte(`{`)},
|
||||||
}))
|
}))
|
||||||
if err == nil || !strings.Contains(err.Error(), "my/data/word.json") {
|
if err == nil || !strings.Contains(err.Error(), "my.data (word.json)") {
|
||||||
t.Errorf("New = %v, want the broken file named under its namespace", err)
|
t.Errorf("New = %v, want the broken file named under its namespace", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -505,9 +433,7 @@ func TestABrokenManifestFailsNewNamingEveryMistake(t *testing.T) {
|
|||||||
`{"index": {"a": {"paths": ["", "x", "x"]}}}`: {`paths item 3 repeats "x"`},
|
`{"index": {"a": {"paths": ["", "x", "x"]}}}`: {`paths item 3 repeats "x"`},
|
||||||
`{"index": {"a": {"paths": [""], "parent": "b"}}}`: {`parent "b" names no entry`},
|
`{"index": {"a": {"paths": [""], "parent": "b"}}}`: {`parent "b" names no entry`},
|
||||||
`{"index": {"a": {"paths": [""], "reads": ["b"]}}}`: {`unknown key "reads"`},
|
`{"index": {"a": {"paths": [""], "reads": ["b"]}}}`: {`unknown key "reads"`},
|
||||||
`{"reads": "b"}`: {"reads must be a list, not a string"},
|
`{"reads": ["b"]}`: {`unknown key "reads"; a manifest holds index and namespace`},
|
||||||
`{"reads": ["", 3]}`: {"reads item 1:", "reads item 2 must be a string, not a number"},
|
|
||||||
`{"reads": ["a", "a"]}`: {`reads item 2 repeats "a"`},
|
|
||||||
`{"namespace": 5}`: {"namespace must be a string, not a number"},
|
`{"namespace": 5}`: {"namespace must be a string, not a number"},
|
||||||
`{"namespace": "a..b"}`: {`namespace "a..b"`},
|
`{"namespace": "a..b"}`: {`namespace "a..b"`},
|
||||||
`{"index": {"b.x": {"paths": [""]}}, "namespace": "a"}`: {`index entry "b.x" lies outside the namespace "a"`},
|
`{"index": {"b.x": {"paths": [""]}}, "namespace": "a"}`: {`index entry "b.x" lies outside the namespace "a"`},
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ func (f *Generator) structShapeOf(t reflect.Type) (*structShape, error) {
|
|||||||
if label == "" {
|
if label == "" {
|
||||||
label = "struct"
|
label = "struct"
|
||||||
}
|
}
|
||||||
sc := &structCompile{root: &f.root, reads: f.manifestReads, visiting: map[reflect.Type]bool{}, structsLeft: maxStructs}
|
sc := &structCompile{root: &f.root, visiting: map[reflect.Type]bool{}, structsLeft: maxStructs}
|
||||||
shape, err := sc.compileShape(t, label)
|
shape, err := sc.compileShape(t, label)
|
||||||
if f.structs == nil {
|
if f.structs == nil {
|
||||||
f.structs = map[reflect.Type]structResult{}
|
f.structs = map[reflect.Type]structResult{}
|
||||||
@@ -85,7 +85,6 @@ func (s *structShape) empty() bool { return s.record == nil && len(s.nested) ==
|
|||||||
// rather than filled without end, and how many more structs it may walk.
|
// rather than filled without end, and how many more structs it may walk.
|
||||||
type structCompile struct {
|
type structCompile struct {
|
||||||
root *folder
|
root *folder
|
||||||
reads []string
|
|
||||||
visiting map[reflect.Type]bool
|
visiting map[reflect.Type]bool
|
||||||
structsLeft int
|
structsLeft int
|
||||||
}
|
}
|
||||||
@@ -112,7 +111,7 @@ func (sc *structCompile) compileShape(t reflect.Type, label string) (*structShap
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(c.tags) > 0 {
|
if len(c.tags) > 0 {
|
||||||
if err := c.shape.compileRecord(sc.root, sc.reads, t, label, c.tags); err != nil {
|
if err := c.shape.compileRecord(sc.root, t, label, c.tags); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -256,13 +255,13 @@ func checkTaggedType(sf reflect.StructField) error {
|
|||||||
|
|
||||||
// compileRecord compiles the tagged fields of t as one record, and proves each column holds
|
// compileRecord compiles the tagged fields of t as one record, and proves each column holds
|
||||||
// only what its field's Go type can.
|
// only what its field's Go type can.
|
||||||
func (s *structShape) compileRecord(root *folder, reads []string, t reflect.Type, label string, tags map[string]jsonvalue.Value) error {
|
func (s *structShape) compileRecord(root *folder, t reflect.Type, label string, tags map[string]jsonvalue.Value) error {
|
||||||
tags["format"] = jsonvalue.String("")
|
tags["format"] = jsonvalue.String("")
|
||||||
n, err := compileFrom(jsonvalue.Object(tags), atGoTop)
|
n, err := compileFrom(jsonvalue.Object(tags), atGoTop)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %w", label, err)
|
return fmt.Errorf("%s: %w", label, err)
|
||||||
}
|
}
|
||||||
if err := loadInline(root, reads, n, label, true); err != nil {
|
if err := loadInline(root, n, label, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
record, err := recordOf(n)
|
record, err := recordOf(n)
|
||||||
|
|||||||
Reference in New Issue
Block a user