diff --git a/README.md b/README.md index 5b54247..a7d5f49 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ template holding one from a quoted heredoc or a file. The rest of stdin's rules | `-d`, `--data-path D` | a directory to layer over the shipped data; repeatable, the last wins a name clash | | `--no-shipped-data` | load only the `--data-path` directories | | `-s`, `--seed N` | same seed, version and data: identical output | -| `-n`, `--repeat N` | render the template N times (up to 1048576), each an independent draw, streamed | +| `-n`, `--repeat N` | render the template N times (0 to 1048576), each an independent draw, streamed; `0` prints nothing, or `[]` under `--format json` | | `--separator S` | between repeated renders (default empty); a record `--format` ignores it | | `--format F` | `text` (default), `json`, `ndjson`, `csv` or `sql` — a record's columns, one record per row (json frames them as an array) | | `--table T` | the INSERT target for `--format sql`, ignored under any other (default: a lone reference's last segment, such as `users` for `{/users}`, else `records`) | diff --git a/cmd/fejkdata/main.go b/cmd/fejkdata/main.go index 505b6d7..294fcce 100644 --- a/cmd/fejkdata/main.go +++ b/cmd/fejkdata/main.go @@ -61,7 +61,7 @@ one INSERT. --list list the paths the data offers, each written after {/ in a template, then exit; --repeat, --separator, --format and --table do nothing here --no-shipped-data load only the --data-path directories - -n, --repeat N render the template N times, 1..1048576 (default 1) + -n, --repeat N render the template N times, 0..1048576 (default 1) -s, --seed N same seed, version and data: identical output --separator S string between repeated renders (default empty); a record --format ignores it --table T the INSERT target for --format sql, ignored under any other @@ -103,8 +103,8 @@ var flagDefs = []flagDef{ {"no-shipped-data", "", false, func(in *invocation, _ string) error { in.noShipped = true; return nil }}, {"repeat", "n", true, func(in *invocation, v string) error { n, err := strconv.Atoi(v) - if err != nil || n < 1 || n > fejkdata.MaxRepeat { - return fmt.Errorf("--repeat needs an integer in 1..%d, got %q", fejkdata.MaxRepeat, v) + if err != nil || n < 0 || n > fejkdata.MaxRepeat { + return fmt.Errorf("--repeat needs an integer in 0..%d, got %q", fejkdata.MaxRepeat, v) } in.repeat = n return nil @@ -370,6 +370,9 @@ func (in invocation) writeRecords(f *fejkdata.Generator, src input, w io.Writer) return err } format := recordFormats[in.format] + if in.repeat == 0 { + return writeNoRecord(record, format, w) + } out := bufio.NewWriter(w) if format.open != "" { out.WriteString(format.open) @@ -397,6 +400,19 @@ func (in invocation) writeRecords(f *fejkdata.Generator, src input, w io.Writer) return out.Flush() } +// writeNoRecord writes zero records: an empty document where the format frames one, else nothing. It +// draws one record and drops it, so a lone reference to nothing fails as it would with records. +func writeNoRecord(record func() (*fejkdata.Record, error), format recordFormat, w io.Writer) error { + if _, err := record(); err != nil { + return err + } + if format.open == "" { + return nil + } + _, err := io.WriteString(w, format.open+format.close+"\n") + return err +} + // defaultTable names the INSERT target when --table is absent: the path's last // segment, or "records" when stdin is not a lone reference. func defaultTable(path string) string {