Add CLI -repeat and -separator flags for emitting multiple values
This commit is contained in:
@@ -36,8 +36,13 @@ fakes ./data/sv_SE person.last # Eriksson (dotted path into a category)
|
|||||||
fakes ./data sv_SE.person # point at the tree; the folder is a segment
|
fakes ./data sv_SE.person # point at the tree; the folder is a segment
|
||||||
fakes ./data/sv_SE ./mydata word # layer dirs; the last wins a name clash
|
fakes ./data/sv_SE ./mydata word # layer dirs; the last wins a name clash
|
||||||
fakes -seed 42 ./data/sv_SE address
|
fakes -seed 42 ./data/sv_SE address
|
||||||
|
fakes -repeat 3 ./data/sv_SE person # three values, one per line
|
||||||
|
fakes -repeat 3 -separator ', ' ./data/sv_SE word # nät, barn, sol
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`-repeat N` renders the path N times — each an independent draw — joined by
|
||||||
|
`-separator` (default a newline, so values land one per line).
|
||||||
|
|
||||||
Without installing, run it from a checkout with `go run ./cmd/fakes …`. Exit
|
Without installing, run it from a checkout with `go run ./cmd/fakes …`. Exit
|
||||||
codes: `0` success, `1` runtime error (missing dir, unknown path), `2` misuse.
|
codes: `0` success, `1` runtime error (missing dir, unknown path), `2` misuse.
|
||||||
|
|
||||||
@@ -68,11 +73,11 @@ fakes -seed 1 ./data/sv_SE sql
|
|||||||
# INSERT INTO users VALUES('zoom'),('wahoo'),('blip');
|
# INSERT INTO users VALUES('zoom'),('wahoo'),('blip');
|
||||||
```
|
```
|
||||||
|
|
||||||
Raise `repeat` for more rows per statement, or loop in the shell to build a
|
Raise the template's `repeat` for more rows per statement; use the CLI's
|
||||||
whole seed file:
|
`-repeat` for more statements — together they build a whole seed file:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
for _ in $(seq 100); do fakes ./data/sv_SE sql; done > seed.sql
|
fakes -repeat 100 ./data/sv_SE sql > seed.sql
|
||||||
```
|
```
|
||||||
|
|
||||||
## Library
|
## Library
|
||||||
|
|||||||
+20
-9
@@ -14,15 +14,18 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/Timewave-AB/fakes"
|
"github.com/Timewave-AB/fakes"
|
||||||
)
|
)
|
||||||
|
|
||||||
const usage = `Usage: fakes [-seed N] <data-dir>... <path>
|
const usage = `Usage: fakes [-seed N] [-repeat N] [-separator S] <data-dir>... <path>
|
||||||
|
|
||||||
<data-dir> one or more data directories, e.g. ./data/sv_SE (last wins on clash)
|
<data-dir> one or more data directories, e.g. ./data/sv_SE (last wins on clash)
|
||||||
<path> a category, or a dotted path into one (person, person.last)
|
<path> a category, or a dotted path into one (person, person.last)
|
||||||
-seed N seed for reproducible output`
|
-seed N seed for reproducible output
|
||||||
|
-repeat N render the path N times (default 1)
|
||||||
|
-separator S string between repeated values (default newline)`
|
||||||
|
|
||||||
func main() { os.Exit(run(os.Args[1:], os.Stdout, os.Stderr)) }
|
func main() { os.Exit(run(os.Args[1:], os.Stdout, os.Stderr)) }
|
||||||
|
|
||||||
@@ -33,6 +36,8 @@ func run(args []string, stdout, stderr io.Writer) int {
|
|||||||
fs.SetOutput(stderr)
|
fs.SetOutput(stderr)
|
||||||
fs.Usage = func() { fmt.Fprintln(stderr, usage) }
|
fs.Usage = func() { fmt.Fprintln(stderr, usage) }
|
||||||
seed := fs.Uint64("seed", 0, "seed for reproducible output")
|
seed := fs.Uint64("seed", 0, "seed for reproducible output")
|
||||||
|
repeat := fs.Int("repeat", 1, "render the path this many times")
|
||||||
|
sep := fs.String("separator", "\n", "string between repeated values")
|
||||||
if err := fs.Parse(args); err != nil {
|
if err := fs.Parse(args); err != nil {
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
@@ -40,6 +45,10 @@ func run(args []string, stdout, stderr io.Writer) int {
|
|||||||
fs.Usage()
|
fs.Usage()
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
if *repeat < 1 {
|
||||||
|
fmt.Fprintln(stderr, "repeat must be a positive integer")
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
var opts []fakes.Option
|
var opts []fakes.Option
|
||||||
fs.Visit(func(fl *flag.Flag) {
|
fs.Visit(func(fl *flag.Flag) {
|
||||||
@@ -54,11 +63,13 @@ func run(args []string, stdout, stderr io.Writer) int {
|
|||||||
fmt.Fprintln(stderr, err)
|
fmt.Fprintln(stderr, err)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
v, err := f.Fake(path)
|
vals := make([]string, *repeat)
|
||||||
if err != nil {
|
for i := range vals {
|
||||||
fmt.Fprintln(stderr, err)
|
if vals[i], err = f.Fake(path); err != nil {
|
||||||
return 1
|
fmt.Fprintln(stderr, err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fmt.Fprintln(stdout, v)
|
fmt.Fprintln(stdout, strings.Join(vals, *sep))
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,60 @@ func TestRunSeedDeterministic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunRepeat(t *testing.T) {
|
||||||
|
// -repeat N prints N values, one per line by default.
|
||||||
|
code, out, errb := runOut("-seed", "1", "-repeat", "3", svSE, "word")
|
||||||
|
if code != 0 {
|
||||||
|
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||||
|
}
|
||||||
|
lines := strings.Split(strings.TrimRight(out, "\n"), "\n")
|
||||||
|
if len(lines) != 3 {
|
||||||
|
t.Errorf("repeat=3 gave %d lines: %q", len(lines), out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunSeparator(t *testing.T) {
|
||||||
|
// -separator joins the repeated values instead of newlines.
|
||||||
|
code, out, errb := runOut("-repeat", "3", "-separator", ",", svSE, "word")
|
||||||
|
if code != 0 {
|
||||||
|
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||||
|
}
|
||||||
|
if n := strings.Count(out, "\n"); n != 1 {
|
||||||
|
t.Errorf("want one trailing newline, got %d: %q", n, out)
|
||||||
|
}
|
||||||
|
if !strings.Contains(out, ",") {
|
||||||
|
t.Errorf("values should be comma-joined: %q", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunRepeatAdvancesRNG(t *testing.T) {
|
||||||
|
// Each repeat is a fresh draw, not the same value N times.
|
||||||
|
code, out, errb := runOut("-seed", "1", "-repeat", "5", svSE, "person")
|
||||||
|
if code != 0 {
|
||||||
|
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||||
|
}
|
||||||
|
uniq := map[string]bool{}
|
||||||
|
for _, l := range strings.Split(strings.TrimRight(out, "\n"), "\n") {
|
||||||
|
uniq[l] = true
|
||||||
|
}
|
||||||
|
if len(uniq) < 2 {
|
||||||
|
t.Errorf("repeat should vary output, all identical: %q", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunRepeatInvalid(t *testing.T) {
|
||||||
|
// A non-positive repeat is misuse.
|
||||||
|
for _, r := range []string{"0", "-1"} {
|
||||||
|
code, _, errb := runOut("-repeat", r, svSE, "word")
|
||||||
|
if code != 2 {
|
||||||
|
t.Errorf("repeat=%s = %d, want 2", r, code)
|
||||||
|
}
|
||||||
|
if errb == "" {
|
||||||
|
t.Errorf("repeat=%s: want an error message", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRunUsageOnTooFewArgs(t *testing.T) {
|
func TestRunUsageOnTooFewArgs(t *testing.T) {
|
||||||
// Need at least one data dir plus a path; fewer is misuse.
|
// Need at least one data dir plus a path; fewer is misuse.
|
||||||
for _, args := range [][]string{{}, {svSE}} {
|
for _, args := range [][]string{{}, {svSE}} {
|
||||||
|
|||||||
Reference in New Issue
Block a user