feat(app): make -json opt-in via App.JSON (v1.3.0)
App now registers -json only when App.JSON is true, so a CLI whose commands do not use cli.Emit never advertises a flag it cannot honor. Driven by the first real consumer (evpnc), whose commands print text directly and are not yet converted to Emit. The example opts in (JSON: true). Backward-additive: existing App users that want -json set JSON: true. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -55,6 +55,10 @@ type App[C any] struct {
|
|||||||
Root *Node[C]
|
Root *Node[C]
|
||||||
// Greeting, if set, is printed after the version banner in interactive mode.
|
// Greeting, if set, is printed after the version banner in interactive mode.
|
||||||
Greeting string
|
Greeting string
|
||||||
|
// JSON, when true, registers a -json flag that switches Emit to JSON output.
|
||||||
|
// Leave it false for CLIs whose commands do not (yet) use cli.Emit, so they
|
||||||
|
// never advertise a flag they cannot honor.
|
||||||
|
JSON bool
|
||||||
|
|
||||||
// DefaultServer is the -server default. If both it and ServerEnv are empty,
|
// DefaultServer is the -server default. If both it and ServerEnv are empty,
|
||||||
// no -server flag is registered (local CLI).
|
// no -server flag is registered (local CLI).
|
||||||
@@ -109,7 +113,10 @@ func (a *App[C]) Run(ctx context.Context, argv []string) error {
|
|||||||
serverFlag = fs.String("server", defaultServer, usage)
|
serverFlag = fs.String("server", defaultServer, usage)
|
||||||
}
|
}
|
||||||
color := fs.Bool("color", true, "colorize output (default: on in the shell, off one-shot)")
|
color := fs.Bool("color", true, "colorize output (default: on in the shell, off one-shot)")
|
||||||
jsonOut := fs.Bool("json", false, "emit JSON instead of text")
|
var jsonOut *bool
|
||||||
|
if a.JSON {
|
||||||
|
jsonOut = fs.Bool("json", false, "emit JSON instead of text")
|
||||||
|
}
|
||||||
showVersion := fs.Bool("version", false, "print version and exit")
|
showVersion := fs.Bool("version", false, "print version and exit")
|
||||||
if a.RegisterFlags != nil {
|
if a.RegisterFlags != nil {
|
||||||
a.RegisterFlags(fs)
|
a.RegisterFlags(fs)
|
||||||
@@ -125,7 +132,8 @@ func (a *App[C]) Run(ctx context.Context, argv []string) error {
|
|||||||
fmt.Println(a.versionLine())
|
fmt.Println(a.versionLine())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if *jsonOut {
|
jsonMode := jsonOut != nil && *jsonOut
|
||||||
|
if jsonMode {
|
||||||
SetFormat(FormatJSON)
|
SetFormat(FormatJSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +153,7 @@ func (a *App[C]) Run(ctx context.Context, argv []string) error {
|
|||||||
if colorExplicit {
|
if colorExplicit {
|
||||||
colorOn = *color
|
colorOn = *color
|
||||||
}
|
}
|
||||||
if *jsonOut {
|
if jsonMode {
|
||||||
colorOn = false
|
colorOn = false
|
||||||
}
|
}
|
||||||
SetColor(colorOn)
|
SetColor(colorOn)
|
||||||
|
|||||||
+4
-3
@@ -7,7 +7,7 @@ SPDX-License-Identifier: Apache-2.0
|
|||||||
|
|
||||||
| | |
|
| | |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| **Status** | Describes shipped behavior as of `v1.2.0` |
|
| **Status** | Describes shipped behavior as of `v1.3.0` |
|
||||||
| **Author** | Pim van Pelt `<pim@ipng.ch>` |
|
| **Author** | Pim van Pelt `<pim@ipng.ch>` |
|
||||||
| **Last updated** | 2026-06-05 |
|
| **Last updated** | 2026-06-05 |
|
||||||
| **Audience** | Contributors, and authors of CLIs built on this library |
|
| **Audience** | Contributors, and authors of CLIs built on this library |
|
||||||
@@ -95,8 +95,9 @@ or JSON from the same code.
|
|||||||
|
|
||||||
**FR-5 `App` entry point**
|
**FR-5 `App` entry point**
|
||||||
|
|
||||||
- **FR-5.1** `App` MUST register `-color`, `-json`, `-version`, and `-server`
|
- **FR-5.1** `App` MUST register `-color` and `-version`; `-server` only when a
|
||||||
(only when a server is configured).
|
server is configured; and `-json` only when the app opts in (its commands use
|
||||||
|
`Emit`), so a CLI never advertises a flag it cannot honor.
|
||||||
- **FR-5.2** `App` MUST NOT dial anything; the client is built in a caller's
|
- **FR-5.2** `App` MUST NOT dial anything; the client is built in a caller's
|
||||||
`Connect` callback.
|
`Connect` callback.
|
||||||
- **FR-5.3** `-version` MUST print and exit without connecting.
|
- **FR-5.3** `-version` MUST print and exit without connecting.
|
||||||
|
|||||||
+2
-1
@@ -201,9 +201,10 @@ func buildTree() *cli.Node[inventory] {
|
|||||||
func main() {
|
func main() {
|
||||||
(&cli.App[inventory]{
|
(&cli.App[inventory]{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Version: "1.1.0",
|
Version: "1.3.0",
|
||||||
Prompt: "inv> ",
|
Prompt: "inv> ",
|
||||||
Root: buildTree(),
|
Root: buildTree(),
|
||||||
|
JSON: true, // commands use cli.Emit, so advertise -json
|
||||||
Greeting: "golang-cli example — try: show server, ping db1, colors, '?' for help, TAB to complete",
|
Greeting: "golang-cli example — try: show server, ping db1, colors, '?' for help, TAB to complete",
|
||||||
// Local CLI: no -server flag. Connect just hands over the in-memory data,
|
// Local CLI: no -server flag. Connect just hands over the in-memory data,
|
||||||
// proving App is transport-agnostic (it never dials anything itself).
|
// proving App is transport-agnostic (it never dials anything itself).
|
||||||
|
|||||||
Reference in New Issue
Block a user