Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c8f30406e |
@@ -44,7 +44,7 @@ level — folders, then the category (a JSON file), then fields.
|
|||||||
| `-d`, `--data-path D` | a directory to layer over the shipped data; repeatable, the last wins a name clash |
|
| `-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 |
|
| `--no-shipped-data` | load only the `--data-path` directories |
|
||||||
| `-s`, `--seed N` | reproducible output |
|
| `-s`, `--seed N` | reproducible output |
|
||||||
| `-n`, `--repeat N` | render the path N times (up to 1048576), each an independent draw, streamed |
|
| `-n`, `--repeat N` | render the path N times, each an independent draw |
|
||||||
| `--separator S` | between repeated values (default a newline) |
|
| `--separator S` | between repeated values (default a newline) |
|
||||||
| `--list` | print every path, then exit |
|
| `--list` | print every path, then exit |
|
||||||
| `--version`, `-h`, `--help` | print, then exit |
|
| `--version`, `-h`, `--help` | print, then exit |
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ import (
|
|||||||
// fat-fingered or overflowing argument fails at New instead of trying to allocate
|
// fat-fingered or overflowing argument fails at New instead of trying to allocate
|
||||||
// gigabytes — or panicking — at render.
|
// gigabytes — or panicking — at render.
|
||||||
const (
|
const (
|
||||||
maxLen = MaxRepeat
|
maxLen = 1 << 20 // 1,048,576 chars/bytes
|
||||||
maxDecimals = 1024
|
maxDecimals = 1024
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+18
-28
@@ -8,7 +8,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -28,7 +27,7 @@ const usage = `Usage: fejkdata [flags] <path>
|
|||||||
-h, --help print this help, then exit
|
-h, --help print this help, then exit
|
||||||
--list list the paths the data offers, then exit
|
--list list the paths the data offers, then exit
|
||||||
--no-shipped-data load only the --data-path directories
|
--no-shipped-data load only the --data-path directories
|
||||||
-n, --repeat N render the path N times, 1..1048576 (default 1)
|
-n, --repeat N render the path N times (default 1)
|
||||||
-s, --seed N seed for reproducible output
|
-s, --seed N seed for reproducible output
|
||||||
--separator S string between repeated values (default newline)
|
--separator S string between repeated values (default newline)
|
||||||
--version print the version, then exit
|
--version print the version, then exit
|
||||||
@@ -44,11 +43,9 @@ type invocation struct {
|
|||||||
noShipped bool
|
noShipped bool
|
||||||
paths []string
|
paths []string
|
||||||
repeat int
|
repeat int
|
||||||
repeatSet bool
|
|
||||||
seed uint64
|
seed uint64
|
||||||
seeded bool
|
seeded bool
|
||||||
separator string
|
separator string
|
||||||
separatorSet bool
|
|
||||||
version bool
|
version bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,10 +63,10 @@ var flagDefs = []flagDef{
|
|||||||
{"no-shipped-data", "", false, func(in *invocation, _ string) error { in.noShipped = true; return nil }},
|
{"no-shipped-data", "", false, func(in *invocation, _ string) error { in.noShipped = true; return nil }},
|
||||||
{"repeat", "n", true, func(in *invocation, v string) error {
|
{"repeat", "n", true, func(in *invocation, v string) error {
|
||||||
n, err := strconv.Atoi(v)
|
n, err := strconv.Atoi(v)
|
||||||
if err != nil || n < 1 || n > fejkdata.MaxRepeat {
|
if err != nil || n < 1 {
|
||||||
return fmt.Errorf("--repeat needs an integer in 1..%d, got %q", fejkdata.MaxRepeat, v)
|
return fmt.Errorf("--repeat needs a positive integer, got %q", v)
|
||||||
}
|
}
|
||||||
in.repeat, in.repeatSet = n, true
|
in.repeat = n
|
||||||
return nil
|
return nil
|
||||||
}},
|
}},
|
||||||
{"seed", "s", true, func(in *invocation, v string) error {
|
{"seed", "s", true, func(in *invocation, v string) error {
|
||||||
@@ -80,7 +77,7 @@ var flagDefs = []flagDef{
|
|||||||
in.seed, in.seeded = n, true
|
in.seed, in.seeded = n, true
|
||||||
return nil
|
return nil
|
||||||
}},
|
}},
|
||||||
{"separator", "", true, func(in *invocation, v string) error { in.separator, in.separatorSet = v, true; return nil }},
|
{"separator", "", true, func(in *invocation, v string) error { in.separator = v; return nil }},
|
||||||
{"version", "", false, func(in *invocation, _ string) error { in.version = true; return nil }},
|
{"version", "", false, func(in *invocation, _ string) error { in.version = true; return nil }},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,8 +124,8 @@ func splitFlags(arg string) ([]flagArg, error) {
|
|||||||
}
|
}
|
||||||
var flags []flagArg
|
var flags []flagArg
|
||||||
letters := arg[1:]
|
letters := arg[1:]
|
||||||
for i, r := range letters {
|
for i := 0; i < len(letters); i++ {
|
||||||
letter := string(r)
|
letter := letters[i : i+1]
|
||||||
def := flagByShort(letter)
|
def := flagByShort(letter)
|
||||||
if def == nil {
|
if def == nil {
|
||||||
return nil, fmt.Errorf("unknown flag -%s", letter)
|
return nil, fmt.Errorf("unknown flag -%s", letter)
|
||||||
@@ -137,7 +134,7 @@ func splitFlags(arg string) ([]flagArg, error) {
|
|||||||
flags = append(flags, flagArg{def: def})
|
flags = append(flags, flagArg{def: def})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
rest := letters[i+len(letter):]
|
rest := letters[i+1:]
|
||||||
if strings.HasPrefix(rest, "=") {
|
if strings.HasPrefix(rest, "=") {
|
||||||
return nil, fmt.Errorf("-%s takes its value attached (-%s%s) or next (-%s %s); = belongs to --%s=%s",
|
return nil, fmt.Errorf("-%s takes its value attached (-%s%s) or next (-%s %s); = belongs to --%s=%s",
|
||||||
letter, letter, rest[1:], letter, rest[1:], def.long, rest[1:])
|
letter, letter, rest[1:], letter, rest[1:], def.long, rest[1:])
|
||||||
@@ -191,9 +188,6 @@ func (in invocation) check() error {
|
|||||||
if in.list && len(in.paths) > 0 {
|
if in.list && len(in.paths) > 0 {
|
||||||
return errors.New("--list takes no path")
|
return errors.New("--list takes no path")
|
||||||
}
|
}
|
||||||
if in.list && (in.repeatSet || in.separatorSet) {
|
|
||||||
return errors.New("--list takes no --repeat or --separator")
|
|
||||||
}
|
|
||||||
if !in.list && len(in.paths) != 1 {
|
if !in.list && len(in.paths) != 1 {
|
||||||
return fmt.Errorf("expected one path, got %d", len(in.paths))
|
return fmt.Errorf("expected one path, got %d", len(in.paths))
|
||||||
}
|
}
|
||||||
@@ -214,23 +208,17 @@ func (in invocation) options() []fejkdata.Option {
|
|||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
// write streams the path's renders to w, repeat of them joined by the separator
|
// values renders the path repeat times, joined by the separator.
|
||||||
// and ended by a newline. A path that renders once renders every time, so the only
|
func (in invocation) values(f *fejkdata.Generator) (string, error) {
|
||||||
// failure comes before anything is written.
|
vals := make([]string, in.repeat)
|
||||||
func (in invocation) write(f *fejkdata.Generator, w io.Writer) error {
|
for i := range vals {
|
||||||
out := bufio.NewWriter(w)
|
|
||||||
for i := 0; i < in.repeat; i++ {
|
|
||||||
v, err := f.Fake(in.paths[0])
|
v, err := f.Fake(in.paths[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
if i > 0 {
|
vals[i] = v
|
||||||
out.WriteString(in.separator)
|
|
||||||
}
|
}
|
||||||
out.WriteString(v)
|
return strings.Join(vals, in.separator), nil
|
||||||
}
|
|
||||||
out.WriteString("\n")
|
|
||||||
return out.Flush()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() { os.Exit(run(os.Args[1:], os.Stdout, os.Stderr)) }
|
func main() { os.Exit(run(os.Args[1:], os.Stdout, os.Stderr)) }
|
||||||
@@ -266,10 +254,12 @@ func run(args []string, stdout, stderr io.Writer) int {
|
|||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
if err := in.write(f, stdout); err != nil {
|
out, err := in.values(f)
|
||||||
|
if err != nil {
|
||||||
fmt.Fprintln(stderr, err)
|
fmt.Fprintln(stderr, err)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
fmt.Fprintln(stdout, out)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ func TestRunShortFlagValues(t *testing.T) {
|
|||||||
{[]string{"-s=42", "-d", svSE, "address"}, "-s42"},
|
{[]string{"-s=42", "-d", svSE, "address"}, "-s42"},
|
||||||
{[]string{"-s=42", "-d", svSE, "address"}, "--seed=42"},
|
{[]string{"-s=42", "-d", svSE, "address"}, "--seed=42"},
|
||||||
{[]string{"-d=" + svSE, "address"}, "--data-path="},
|
{[]string{"-d=" + svSE, "address"}, "--data-path="},
|
||||||
{[]string{"-nd", "3", "-d", svSE, "word"}, `--repeat needs an integer in 1..1048576, got "d"`},
|
{[]string{"-nd", "3", "-d", svSE, "word"}, `--repeat needs a positive integer, got "d"`},
|
||||||
{[]string{"--seed=", "-d", svSE, "word"}, `--seed needs an unsigned integer, got ""`},
|
{[]string{"--seed=", "-d", svSE, "word"}, `--seed needs an unsigned integer, got ""`},
|
||||||
} {
|
} {
|
||||||
code, out, errb := runOut(c.args...)
|
code, out, errb := runOut(c.args...)
|
||||||
|
|||||||
@@ -28,9 +28,6 @@ import (
|
|||||||
//go:embed data
|
//go:embed data
|
||||||
var shippedFS embed.FS
|
var shippedFS embed.FS
|
||||||
|
|
||||||
// MaxRepeat caps a repeat, and the renders nested repeats multiply to along any path.
|
|
||||||
const MaxRepeat = 1 << 20
|
|
||||||
|
|
||||||
// ErrNoData is returned by New when no source is loaded at all.
|
// ErrNoData is returned by New when no source is loaded at all.
|
||||||
var ErrNoData = errors.New("no data: WithoutShippedData needs at least one WithDataPath or WithDataFS")
|
var ErrNoData = errors.New("no data: WithoutShippedData needs at least one WithDataPath or WithDataFS")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user