Fix the column set once, drop the unreachable separator override, and name the record predicate apart from the format table
Tests / vet + fmt + tests (pull_request) Successful in 1m2s
Tests / vet + fmt + tests (pull_request) Successful in 1m2s
This commit is contained in:
@@ -225,8 +225,8 @@ var recordFormats = map[string]recordFormat{
|
|||||||
"sql": {line: func(r *fejkdata.Record, table string) string { return r.SQLInsert(table) }},
|
"sql": {line: func(r *fejkdata.Record, table string) string { return r.SQLInsert(table) }},
|
||||||
}
|
}
|
||||||
|
|
||||||
// recordFormat reports whether the format writes records rather than plain text.
|
// writesRecords reports whether the format writes records rather than plain text.
|
||||||
func (in invocation) recordFormat() bool {
|
func (in invocation) writesRecords() bool {
|
||||||
return in.format != "text"
|
return in.format != "text"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,7 +248,7 @@ func (in invocation) checkFlags() error {
|
|||||||
if in.tableSet && in.format != "sql" {
|
if in.tableSet && in.format != "sql" {
|
||||||
return errors.New("--table names the INSERT target, so it needs --format sql")
|
return errors.New("--table names the INSERT target, so it needs --format sql")
|
||||||
}
|
}
|
||||||
if in.recordFormat() && in.separatorSet {
|
if in.writesRecords() && in.separatorSet {
|
||||||
return errors.New("--separator joins text values, so it has no effect with --format " + in.format)
|
return errors.New("--separator joins text values, so it has no effect with --format " + in.format)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -298,10 +298,6 @@ func (in invocation) write(f *fejkdata.Generator, kind argKind, w io.Writer) err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
separator := in.separator
|
|
||||||
if in.recordFormat() {
|
|
||||||
separator = "\n"
|
|
||||||
}
|
|
||||||
out := bufio.NewWriter(w)
|
out := bufio.NewWriter(w)
|
||||||
for i := 0; i < in.repeat; i++ {
|
for i := 0; i < in.repeat; i++ {
|
||||||
v, err := draw()
|
v, err := draw()
|
||||||
@@ -309,7 +305,7 @@ func (in invocation) write(f *fejkdata.Generator, kind argKind, w io.Writer) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
out.WriteString(separator)
|
out.WriteString(in.separator)
|
||||||
}
|
}
|
||||||
out.WriteString(v)
|
out.WriteString(v)
|
||||||
}
|
}
|
||||||
@@ -320,7 +316,7 @@ func (in invocation) write(f *fejkdata.Generator, kind argKind, w io.Writer) err
|
|||||||
// draw builds what one render yields: the value's text, or the record's line in
|
// draw builds what one render yields: the value's text, or the record's line in
|
||||||
// the chosen format, the header carried ahead of the first one.
|
// the chosen format, the header carried ahead of the first one.
|
||||||
func (in invocation) draw(f *fejkdata.Generator, kind argKind, arg string) (func() (string, error), error) {
|
func (in invocation) draw(f *fejkdata.Generator, kind argKind, arg string) (func() (string, error), error) {
|
||||||
if !in.recordFormat() {
|
if !in.writesRecords() {
|
||||||
if kind != argTemplate {
|
if kind != argTemplate {
|
||||||
return func() (string, error) { return f.Fake(arg) }, nil
|
return func() (string, error) { return f.Fake(arg) }, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,11 +100,11 @@ func (f *Generator) Record(path string) (*Record, error) {
|
|||||||
if len(tail) > 0 {
|
if len(tail) > 0 {
|
||||||
return nil, fmt.Errorf("fejkdata: %s descends into %q, a field; only a category-level template is a record", path, tail[0])
|
return nil, fmt.Errorf("fejkdata: %s descends into %q, a field; only a category-level template is a record", path, tail[0])
|
||||||
}
|
}
|
||||||
t, err := recordOf(n)
|
t, columns, err := recordOf(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("fejkdata: %s %w", path, err)
|
return nil, fmt.Errorf("fejkdata: %s %w", path, err)
|
||||||
}
|
}
|
||||||
return renderRecord(f.rand, t), nil
|
return renderRecord(f.rand, t, columns), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecordTemplate is an inline record compiled, referenced and validated once,
|
// RecordTemplate is an inline record compiled, referenced and validated once,
|
||||||
@@ -112,13 +112,14 @@ func (f *Generator) Record(path string) (*Record, error) {
|
|||||||
type RecordTemplate struct {
|
type RecordTemplate struct {
|
||||||
g *Generator
|
g *Generator
|
||||||
t *template
|
t *template
|
||||||
|
columns []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fake renders the record with one draw.
|
// Fake renders the record with one draw.
|
||||||
func (t *RecordTemplate) Fake() *Record {
|
func (t *RecordTemplate) Fake() *Record {
|
||||||
t.g.mu.Lock()
|
t.g.mu.Lock()
|
||||||
defer t.g.mu.Unlock()
|
defer t.g.mu.Unlock()
|
||||||
return renderRecord(t.g.rand, t.t)
|
return renderRecord(t.g.rand, t.t, t.columns)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRecordTemplate compiles an inline record — a JSON object with a format and
|
// NewRecordTemplate compiles an inline record — a JSON object with a format and
|
||||||
@@ -128,11 +129,11 @@ func (f *Generator) NewRecordTemplate(input string) (*RecordTemplate, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rt, err := recordOf(t.n)
|
tm, columns, err := recordOf(t.n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("fejkdata: an inline record %w", err)
|
return nil, fmt.Errorf("fejkdata: an inline record %w", err)
|
||||||
}
|
}
|
||||||
return &RecordTemplate{g: f, t: rt}, nil
|
return &RecordTemplate{g: f, t: tm, columns: columns}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FakeRecord compiles and renders an inline record in one call.
|
// FakeRecord compiles and renders an inline record in one call.
|
||||||
@@ -144,32 +145,33 @@ func (f *Generator) FakeRecord(input string) (*Record, error) {
|
|||||||
return t.Fake(), nil
|
return t.Fake(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// recordOf is the fence both record entry points pass: the node is a
|
// recordOf is the fence both record entry points pass: the node is a template, it
|
||||||
// category-level template, it carries no repeat — which composes the format
|
// carries no repeat — which composes the format rather than projecting columns —
|
||||||
// rather than projecting columns — and it offers at least one column.
|
// and it offers at least one column. The columns come back with it, fixed for
|
||||||
func recordOf(n node) (*template, error) {
|
// every draw the caller goes on to make.
|
||||||
|
func recordOf(n node) (*template, []string, error) {
|
||||||
t, ok := n.(*template)
|
t, ok := n.(*template)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("names a choice, not a template; only a category-level template is a record")
|
return nil, nil, errors.New("names a choice, not a template; a record is a template whose fields are its columns")
|
||||||
}
|
}
|
||||||
if t.repeat != 1 {
|
if t.repeat != 1 {
|
||||||
return nil, fmt.Errorf("carries repeat %d, which composes its format into one string; a record projects columns instead — drop the repeat and render the record again for more rows", t.repeat)
|
return nil, nil, fmt.Errorf("carries repeat %d, which composes its format into one string; a record projects columns instead — drop the repeat and render the record again for more rows", t.repeat)
|
||||||
}
|
}
|
||||||
if len(recordColumns(t)) == 0 {
|
columns := recordColumns(t)
|
||||||
return nil, errors.New("has no fields, so no columns")
|
if len(columns) == 0 {
|
||||||
|
return nil, nil, errors.New("has no fields, so no columns")
|
||||||
}
|
}
|
||||||
return t, nil
|
return t, columns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// renderRecord projects a template's direct fields as columns, drawn once each,
|
// renderRecord draws each column once, in the name order recordOf fixed. The
|
||||||
// in name order. A {/path} binding is a render edge, not a column, so it is
|
// columns share one reference scope, so two columns that reference one category
|
||||||
// skipped the same way List and the graph do. The columns share one reference
|
// read one draw of it.
|
||||||
// scope, so two columns that reference one category read one draw of it.
|
func renderRecord(s *session, t *template, columns []string) *Record {
|
||||||
func renderRecord(s *session, t *template) *Record {
|
|
||||||
scope := &draws{variant: map[string]node{}, value: map[string]string{}}
|
scope := &draws{variant: map[string]node{}, value: map[string]string{}}
|
||||||
r := &Record{}
|
r := &Record{columns: make([]Column, len(columns))}
|
||||||
for _, name := range recordColumns(t) {
|
for i, name := range columns {
|
||||||
r.columns = append(r.columns, Column{Name: name, Value: render(s, t.fields[name], scope)})
|
r.columns[i] = Column{Name: name, Value: render(s, t.fields[name], scope)}
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user