Name the record entry points after the string ones #12

Merged
lilleman merged 2 commits from record-entry-points into main 2026-09-14 23:46:07 +02:00
4 changed files with 11 additions and 13 deletions
Showing only changes of commit 7e789d470f - Show all commits
+6 -6
View File
@@ -82,7 +82,7 @@ For structured output a record writes the row for you.
A record is a template seen as columns: its fields are the columns, its `format` A record is a template seen as columns: its fields are the columns, its `format`
the whole. `--format json|ndjson|csv|sql` writes the records; the library's the whole. `--format json|ndjson|csv|sql` writes the records; the library's
`Record` (below) hands back the columns. Every column is a string — typed scalars `FakeRecord` (below) hands back the columns. Every column is a string — typed scalars
are on the release checklist, see [`todo.md`](todo.md). Save are on the release checklist, see [`todo.md`](todo.md). Save
`mydata/users.json`: `mydata/users.json`:
@@ -152,9 +152,9 @@ paths := f.List() // every path Fake accepts, sorted
v, err = f.FakeTemplate("name: {/sv_SE.person.last}") // compile + render in one call v, err = f.FakeTemplate("name: {/sv_SE.person.last}") // compile + render in one call
t, err := f.NewTemplate(`{"format":"name: {x}","x":["bosse","lina"]}`) // compile once t, err := f.NewTemplate(`{"format":"name: {x}","x":["bosse","lina"]}`) // compile once
v = t.Fake() // render many times, no re-parse v = t.Fake() // render many times, no re-parse
r, err := f.Record("users") // one record: each field a column r, err := f.FakeRecord("users") // one record: each field a column
s := r.JSON() // {"first":"Ada","last":"Lovelace"} s := r.JSON() // {"first":"Ada","last":"Lovelace"}
r, err = f.FakeRecord(`{"format":"{x}","x":["a","b"]}`) // compile + render inline r, err = f.FakeRecordTemplate(`{"format":"{x}","x":["a","b"]}`) // compile + render inline
``` ```
| Option | | | Option | |
@@ -166,8 +166,8 @@ r, err = f.FakeRecord(`{"format":"{x}","x":["a","b"]}`) // compile + render inli
A `*Record` carries its columns via `Columns()`, and serializes them with `JSON()` A `*Record` carries its columns via `Columns()`, and serializes them with `JSON()`
(one object), `CSVHeader()`/`CSVLine()`, or `SQLInsert(table)` — the shapes the (one object), `CSVHeader()`/`CSVLine()`, or `SQLInsert(table)` — the shapes the
CLI's `--format` writes. `Record` and `FakeRecord` take a record; a path or CLI's `--format` writes. `FakeRecord` and `FakeRecordTemplate` take a record; a
template that is not one — a bare string, a choice, or a folder — errors. path or template that is not one — a bare string, a choice, or a folder — errors.
A `*Generator` is safe for concurrent use; a seeded sequence is reproducible only A `*Generator` is safe for concurrent use; a seeded sequence is reproducible only
when drawn from one goroutine. Changing how a value is composed shifts the seeded when drawn from one goroutine. Changing how a value is composed shifts the seeded
@@ -526,7 +526,7 @@ tokens add cost in proportion to the output.
letters, `{uppercase(x)}` is `x` upper-cased; one name for both would turn on letters, `{uppercase(x)}` is `x` upper-cased; one name for both would turn on
whether the argument looks like a number. whether the argument looks like a number.
- **A record is a template seen as columns, not a second schema format.** A - **A record is a template seen as columns, not a second schema format.** A
template's `format` composes its fields into one string; `Record` and template's `format` composes its fields into one string; `FakeRecord` and
`--format` project the same fields as columns. Two views of one dataset, so a `--format` project the same fields as columns. Two views of one dataset, so a
record author writes the same JSON they already know, and a column is the same record author writes the same JSON they already know, and a column is the same
field `Fake` renders by dotted path. The `format` is inert to a record — a field `Fake` renders by dotted path. The `format` is inert to a record — a
+1 -1
View File
@@ -341,7 +341,7 @@ func (in invocation) textDraw(f *fejkdata.Generator, kind argKind, arg string) (
// recordStream builds the record drawer for the argument, plus the INSERT table // recordStream builds the record drawer for the argument, plus the INSERT table
// a sql format names. // a sql format names.
func (in invocation) recordStream(f *fejkdata.Generator, kind argKind, arg string) (func() (*fejkdata.Record, error), string, error) { func (in invocation) recordStream(f *fejkdata.Generator, kind argKind, arg string) (func() (*fejkdata.Record, error), string, error) {
record := func() (*fejkdata.Record, error) { return f.Record(arg) } record := func() (*fejkdata.Record, error) { return f.FakeRecord(arg) }
if kind == argTemplate { if kind == argTemplate {
t, err := f.NewRecordTemplate(arg) t, err := f.NewRecordTemplate(arg)
if err != nil { if err != nil {
+4 -4
View File
@@ -92,10 +92,10 @@ func quoteIdent(s string) string {
return `"` + strings.ReplaceAll(s, `"`, `""`) + `"` return `"` + strings.ReplaceAll(s, `"`, `""`) + `"`
} }
// Record renders a path as one record: the template it names, with each direct // FakeRecord renders a path as one record: the template it names, with each direct
// field drawn as a column. Only a category-level template is a record — a path // field drawn as a column. Only a category-level template is a record — a path
// that descends into a field, or that names a folder or a choice, is an error. // that descends into a field, or that names a folder or a choice, is an error.
func (f *Generator) Record(path string) (*Record, error) { func (f *Generator) FakeRecord(path string) (*Record, error) {
f.mu.Lock() f.mu.Lock()
defer f.mu.Unlock() defer f.mu.Unlock()
_, n, tail, err := resolveCategory(f.categories, strings.Split(path, ".")) _, n, tail, err := resolveCategory(f.categories, strings.Split(path, "."))
@@ -164,8 +164,8 @@ func (f *Generator) NewRecordTemplate(input string) (*RecordTemplate, error) {
return &RecordTemplate{g: f, t: tm, columns: columns}, nil return &RecordTemplate{g: f, t: tm, columns: columns}, nil
} }
// FakeRecord compiles and renders an inline record in one call. // FakeRecordTemplate compiles and renders an inline record in one call.
func (f *Generator) FakeRecord(input string) (*Record, error) { func (f *Generator) FakeRecordTemplate(input string) (*Record, error) {
t, err := f.NewRecordTemplate(input) t, err := f.NewRecordTemplate(input)
if err != nil { if err != nil {
return nil, err return nil, err
-2
View File
@@ -6,8 +6,6 @@ The record API lands first, so the data update can use it.
### Record API ### Record API
- Rename the record entry points after the string ones: `Record(path)` becomes
`FakeRecord(path)`, and `FakeRecord(inline)` becomes `FakeRecordTemplate(inline)`.
- Typed columns — a column declares its type, so `json` writes `42` rather than - Typed columns — a column declares its type, so `json` writes `42` rather than
`"42"` and `sql` an unquoted literal: string, integer, number, boolean, and a `"42"` and `sql` an unquoted literal: string, integer, number, boolean, and a
way to write null. A template that can render a value its type rejects is a way to write null. A template that can render a value its type rejects is a