Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33
@@ -441,14 +441,14 @@ import (
|
|||||||
"github.com/larvit/fejkdata/data"
|
"github.com/larvit/fejkdata/data"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed *.json *.tsv
|
//go:embed *.json
|
||||||
var mydata embed.FS
|
var mydata embed.FS
|
||||||
|
|
||||||
func main() { os.Exit(cli.Main(append(data.Modules(), mydata)...)) }
|
func main() { os.Exit(cli.Main(append(data.Modules(), mydata)...)) }
|
||||||
```
|
```
|
||||||
|
|
||||||
It takes `fejkdata`'s flags and exit codes, and names itself by the file it runs from in
|
Add `*.tsv` to the embed where your data holds a table. The CLI takes `fejkdata`'s flags
|
||||||
its help and errors. `--no-shipped-data` leaves out the modules passed to `Main`, and
|
and exit codes, and names itself by the file it runs from in its help and errors. `--no-shipped-data` leaves out the modules passed to `Main`, and
|
||||||
`--version` prints your module's version beside the fejkdata version it is built with.
|
`--version` prints your module's version beside the fejkdata version it is built with.
|
||||||
`fejkdata`'s own `main` is `os.Exit(cli.Main(data.Modules()...))`.
|
`fejkdata`'s own `main` is `os.Exit(cli.Main(data.Modules()...))`.
|
||||||
|
|
||||||
|
|||||||
+30
-18
@@ -22,16 +22,16 @@ import (
|
|||||||
"github.com/larvit/fejkdata"
|
"github.com/larvit/fejkdata"
|
||||||
)
|
)
|
||||||
|
|
||||||
const usage = `Usage: <template> | fejkdata [flags]
|
const usage = `Usage: <template> | COMMAND [flags]
|
||||||
fejkdata --list | --help | --version
|
COMMAND --list | --help | --version
|
||||||
|
|
||||||
fejkdata reads one template from stdin and renders it:
|
COMMAND reads one template from stdin and renders it:
|
||||||
|
|
||||||
echo 'name: {/sv_SE.person.last}' | fejkdata
|
echo 'name: {/sv_SE.person.last}' | COMMAND
|
||||||
echo '{"format":"{x}","x":["bosse","lina"]}' | fejkdata
|
echo '{"format":"{x}","x":["bosse","lina"]}' | COMMAND
|
||||||
fejkdata < template.txt
|
COMMAND < template.txt
|
||||||
|
|
||||||
fejkdata <<'EOF'
|
COMMAND <<'EOF'
|
||||||
born {date(1990-01-01,2010-12-31,'2006-01-02')}
|
born {date(1990-01-01,2010-12-31,'2006-01-02')}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ path into one ({/sv_SE.person.last}), a table's row by key or name
|
|||||||
({/misc.territory[SE]}), and .. steps up from a row to the row it links to
|
({/misc.territory[SE]}), and .. steps up from a row to the row it links to
|
||||||
({/geo.SE.locality..municipality.name}).
|
({/geo.SE.locality..municipality.name}).
|
||||||
|
|
||||||
Under --format text, the default, what you send is what renders, and fejkdata adds
|
Under --format text, the default, what you send is what renders, and COMMAND adds
|
||||||
nothing: a format string keeps every byte, so echo's newline ends each render, and
|
nothing: a format string keeps every byte, so echo's newline ends each render, and
|
||||||
printf '%s' sends none. -n joins renders with --separator, empty by default. A JSON
|
printf '%s' sends none. -n joins renders with --separator, empty by default. A JSON
|
||||||
object, array or string drops the whitespace around it, and null alone renders nothing;
|
object, array or string drops the whitespace around it, and null alone renders nothing;
|
||||||
@@ -569,7 +569,13 @@ func Main(modules ...fs.FS) int {
|
|||||||
if fi, err := os.Stdin.Stat(); err == nil && fi.Mode()&os.ModeCharDevice != 0 {
|
if fi, err := os.Stdin.Stat(); err == nil && fi.Mode()&os.ModeCharDevice != 0 {
|
||||||
stdin = nil
|
stdin = nil
|
||||||
}
|
}
|
||||||
name := strings.TrimSuffix(filepath.Base(os.Args[0]), ".exe")
|
name := filepath.Base(os.Args[0])
|
||||||
|
if ext := filepath.Ext(name); strings.EqualFold(ext, ".exe") {
|
||||||
|
name = strings.TrimSuffix(name, ext)
|
||||||
|
}
|
||||||
|
if name == "." || name == "" {
|
||||||
|
name = "fejkdata"
|
||||||
|
}
|
||||||
return run(name, modules, os.Args[1:], stdin, os.Stdout, os.Stderr)
|
return run(name, modules, os.Args[1:], stdin, os.Stdout, os.Stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -581,7 +587,7 @@ func run(name string, modules []fs.FS, args []string, stdin io.Reader, stdout, s
|
|||||||
return misuse(stderr, name, err)
|
return misuse(stderr, name, err)
|
||||||
}
|
}
|
||||||
if in.help {
|
if in.help {
|
||||||
io.WriteString(stdout, strings.ReplaceAll(usage, "fejkdata", name))
|
io.WriteString(stdout, strings.ReplaceAll(usage, "COMMAND", name))
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
if in.version {
|
if in.version {
|
||||||
@@ -600,8 +606,7 @@ func run(name string, modules []fs.FS, args []string, stdin io.Reader, stdout, s
|
|||||||
}
|
}
|
||||||
f, err := fejkdata.New(in.options(modules)...)
|
f, err := fejkdata.New(in.options(modules)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(stderr, err)
|
return fail(stderr, name, err)
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
if in.list {
|
if in.list {
|
||||||
for _, p := range f.List() {
|
for _, p := range f.List() {
|
||||||
@@ -639,7 +644,7 @@ func fail(stderr io.Writer, name string, err error) int {
|
|||||||
if errors.As(err, &te) {
|
if errors.As(err, &te) {
|
||||||
return misuse(stderr, name, te.error)
|
return misuse(stderr, name, te.error)
|
||||||
}
|
}
|
||||||
fmt.Fprintln(stderr, err)
|
report(stderr, name, err)
|
||||||
if errors.Is(err, fejkdata.ErrNoColumns) {
|
if errors.Is(err, fejkdata.ErrNoColumns) {
|
||||||
fmt.Fprintln(stderr, "wrap that JSON in single quotes, which keep a shell from expanding its braces")
|
fmt.Fprintln(stderr, "wrap that JSON in single quotes, which keep a shell from expanding its braces")
|
||||||
}
|
}
|
||||||
@@ -647,11 +652,16 @@ func fail(stderr io.Writer, name string, err error) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func misuse(stderr io.Writer, name string, err error) int {
|
func misuse(stderr io.Writer, name string, err error) int {
|
||||||
// A library error opens with the library's name, so the prefix is not doubled.
|
report(stderr, name, err)
|
||||||
fmt.Fprintf(stderr, "%s: %s\ntry '%s --help'\n", name, strings.TrimPrefix(err.Error(), "fejkdata: "), name)
|
fmt.Fprintf(stderr, "try '%s --help'\n", name)
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// report writes err under the command name, in place of the library's own.
|
||||||
|
func report(stderr io.Writer, name string, err error) {
|
||||||
|
fmt.Fprintf(stderr, "%s: %s\n", name, strings.TrimPrefix(err.Error(), "fejkdata: "))
|
||||||
|
}
|
||||||
|
|
||||||
// fejkdataModule is the path of the module holding the library; the root package's path is
|
// fejkdataModule is the path of the module holding the library; the root package's path is
|
||||||
// the module's.
|
// the module's.
|
||||||
var fejkdataModule = reflect.TypeOf((*fejkdata.Generator)(nil)).Elem().PkgPath()
|
var fejkdataModule = reflect.TypeOf((*fejkdata.Generator)(nil)).Elem().PkgPath()
|
||||||
@@ -662,13 +672,15 @@ func versionLine(name string, info *debug.BuildInfo) string {
|
|||||||
if info == nil {
|
if info == nil {
|
||||||
return "fejkdata devel"
|
return "fejkdata devel"
|
||||||
}
|
}
|
||||||
line := "fejkdata " + moduleVersion(info.Main)
|
if info.Main.Path == fejkdataModule {
|
||||||
|
return "fejkdata " + moduleVersion(info.Main)
|
||||||
|
}
|
||||||
for _, m := range info.Deps {
|
for _, m := range info.Deps {
|
||||||
if m.Path == fejkdataModule {
|
if m.Path == fejkdataModule {
|
||||||
line = name + " " + moduleVersion(info.Main) + ", fejkdata " + moduleVersion(*m)
|
return name + " " + moduleVersion(info.Main) + ", fejkdata " + moduleVersion(*m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return line
|
return name + " " + moduleVersion(info.Main) + ", fejkdata devel"
|
||||||
}
|
}
|
||||||
|
|
||||||
// moduleVersion is the version m is built at: the replacement's where a replace directive
|
// moduleVersion is the version m is built at: the replacement's where a replace directive
|
||||||
|
|||||||
@@ -473,6 +473,10 @@ func TestRunNamesItselfByTheCommandName(t *testing.T) {
|
|||||||
if code := run("mycli", shipped, nil, nil, &out, &errb); code != 2 || !strings.Contains(errb.String(), "| mycli\n") {
|
if code := run("mycli", shipped, nil, nil, &out, &errb); code != 2 || !strings.Contains(errb.String(), "| mycli\n") {
|
||||||
t.Errorf("mycli with nothing piped = %d, %q, want a pipe into mycli", code, errb.String())
|
t.Errorf("mycli with nothing piped = %d, %q, want a pipe into mycli", code, errb.String())
|
||||||
}
|
}
|
||||||
|
errb.Reset()
|
||||||
|
if code := run("mycli", shipped, []string{"-d", "/nope"}, strings.NewReader("{/sv_SE.person}"), &out, &errb); code != 1 || !strings.HasPrefix(errb.String(), "mycli: ") {
|
||||||
|
t.Errorf("mycli -d /nope = %d, %q, want a load error under mycli", code, errb.String())
|
||||||
|
}
|
||||||
if code := run("mycli", shipped, []string{"--help"}, nil, &out, &errb); code != 0 || !strings.HasPrefix(out.String(), "Usage: <template> | mycli [flags]\n") || strings.Contains(out.String(), "fejkdata") {
|
if code := run("mycli", shipped, []string{"--help"}, nil, &out, &errb); code != 0 || !strings.HasPrefix(out.String(), "Usage: <template> | mycli [flags]\n") || strings.Contains(out.String(), "fejkdata") {
|
||||||
t.Errorf("mycli --help = %d, %q, want mycli named throughout", code, out.String())
|
t.Errorf("mycli --help = %d, %q, want mycli named throughout", code, out.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -366,7 +366,7 @@ GitHub has no `tags/{tag}` endpoint and answers 404 whether or not the tag exist
|
|||||||
|
|
||||||
### 58. Ship prebuilt binaries, a container image, and packages for Homebrew, Scoop and the biggest Linux package managers, so the CLI needs no Go.
|
### 58. Ship prebuilt binaries, a container image, and packages for Homebrew, Scoop and the biggest Linux package managers, so the CLI needs no Go.
|
||||||
|
|
||||||
GoReleaser attaches the binaries to the release the tag workflow publishes, and builds deb and rpm packages, an Alpine APK and an AUR package beside Homebrew and Scoop. A checkout build prints `devel` for `--version`; the binaries carry the stamped tag. v0.1.0 ships first, so it can be tested before it is packaged.
|
GoReleaser attaches the binaries to the release the tag workflow publishes, and builds deb and rpm packages, an Alpine APK and an AUR package beside Homebrew and Scoop. The binaries carry the stamped tag, which `--version` prints. v0.1.0 ships first, so it can be tested before it is packaged.
|
||||||
|
|
||||||
### 206. Add `zh_CN` with its `geo/CN` tree.
|
### 206. Add `zh_CN` with its `geo/CN` tree.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user