Parse shared debug input once
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Shared debug binding implementation plan
|
# Shared debug binding implementation plan
|
||||||
|
|
||||||
Status: in progress; Stages 1–2 complete.
|
Status: in progress; Stages 1–3 complete.
|
||||||
Baseline: `6727d22ce6e1ac668b56a3e6d8fa5f7a06e54d88`.
|
Baseline: `6727d22ce6e1ac668b56a3e6d8fa5f7a06e54d88`.
|
||||||
Execution audience: Luna coding agent.
|
Execution audience: Luna coding agent.
|
||||||
|
|
||||||
@@ -146,6 +146,11 @@ Backend-dependent checks may remain pending until Stage 3.
|
|||||||
|
|
||||||
## Stage 3 — Parse one debug option into both consumers
|
## Stage 3 — Parse one debug option into both consumers
|
||||||
|
|
||||||
|
**Complete.** Backend registration and interpretation use effective flags while
|
||||||
|
consumer values retain their original indexes. Public mode/value cases, aliases,
|
||||||
|
invalid values, option-looking values and terminated operands pass, as do focused
|
||||||
|
backend parsing checks, offline with Go 1.27.0.
|
||||||
|
|
||||||
Primary files: [backend parse.go](../../internal/cobra/parse.go),
|
Primary files: [backend parse.go](../../internal/cobra/parse.go),
|
||||||
[backend flags.go](../../internal/cobra/flags.go),
|
[backend flags.go](../../internal/cobra/flags.go),
|
||||||
[parser.go](../../parser.go), [binding.go](../../binding.go).
|
[parser.go](../../parser.go), [binding.go](../../binding.go).
|
||||||
|
|||||||
@@ -77,9 +77,7 @@ func (session *Session) Parse(argv []string) commandmeta.Result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
remaining := append(commandpath.CloneTokens(unconsumed), tail...)
|
remaining := append(commandpath.CloneTokens(unconsumed), tail...)
|
||||||
localFlags := make([]commandmeta.Flag, 0, len(selectedNode.Flags)+len(selectedNode.CoreFlags))
|
localFlags := commandmeta.EffectiveFlags(selectedNode.Flags, selectedNode.CoreFlags)
|
||||||
localFlags = append(localFlags, selectedNode.Flags...)
|
|
||||||
localFlags = append(localFlags, selectedNode.CoreFlags...)
|
|
||||||
aliases := session.flagAliases
|
aliases := session.flagAliases
|
||||||
if session.flagAliasesForPath != nil {
|
if session.flagAliasesForPath != nil {
|
||||||
aliases = session.flagAliasesForPath(result.Path)
|
aliases = session.flagAliasesForPath(result.Path)
|
||||||
@@ -316,7 +314,7 @@ func buildTree(root *commandmeta.Command) (*spfcobra.Command, map[*spfcobra.Comm
|
|||||||
helpFlag := backend.Flags().Lookup("help")
|
helpFlag := backend.Flags().Lookup("help")
|
||||||
helpFlag.NoOptDefVal = "true"
|
helpFlag.NoOptDefVal = "true"
|
||||||
helpFlag.Usage = "help for this command (default: false)"
|
helpFlag.Usage = "help for this command (default: false)"
|
||||||
for _, declarationFlag := range append(append([]commandmeta.Flag(nil), declaration.Flags...), declaration.CoreFlags...) {
|
for _, declarationFlag := range commandmeta.EffectiveFlags(declaration.Flags, declaration.CoreFlags) {
|
||||||
value := &rawValue{boolean: declarationFlag.Boolean}
|
value := &rawValue{boolean: declarationFlag.Boolean}
|
||||||
backend.Flags().VarP(value, declarationFlag.Name, declarationFlag.Short, flagUsage(declarationFlag))
|
backend.Flags().VarP(value, declarationFlag.Name, declarationFlag.Short, flagUsage(declarationFlag))
|
||||||
backendFlag := backend.Flags().Lookup(declarationFlag.Name)
|
backendFlag := backend.Flags().Lookup(declarationFlag.Name)
|
||||||
|
|||||||
@@ -281,3 +281,14 @@ func TestMissingSessionTreeReturnsNeutralFailures(t *testing.T) {
|
|||||||
t.Fatalf("missing-tree usage failure = %#v", failure)
|
t.Fatalf("missing-tree usage failure = %#v", failure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSharedDebugExtractionPreservesConsumerIndexes(t *testing.T) {
|
||||||
|
tree := &Command{Name: "app", CoreFlags: commandmeta.ReportingFlags(), Children: []*Command{{Name: "run", Runnable: true, CoreFlags: commandmeta.ReportingFlags(), Flags: []Flag{{Name: "before"}, {Name: "debug", Short: "d", Boolean: true}, {Name: "after"}}}}}
|
||||||
|
result := Parse(tree, []string{"run", "--before=one", "-d", "--after=two", "-x=false"}, ParseOptions{FlagAliases: []commandmeta.FlagAlias{{Source: "-x", Target: "--debug"}}})
|
||||||
|
if result.Failure != nil {
|
||||||
|
t.Fatal(result.Failure)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(result.FlagValues, [][]string{{"one"}, {"true", "false"}, {"two"}}) || !reflect.DeepEqual(result.FlagSpellings[1], []string{"-d", "-x"}) || len(result.FlagOccurrences) != 4 {
|
||||||
|
t.Fatalf("parse = %#v", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -68,3 +68,57 @@ func TestInvocationReportingModeConcurrentReads(t *testing.T) {
|
|||||||
t.Fatal(outcome)
|
t.Fatal(outcome)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSharedDebugBindingAndReportingMode(t *testing.T) {
|
||||||
|
type input struct {
|
||||||
|
debug commandkit.Supplied[bool]
|
||||||
|
before, after commandkit.Supplied[string]
|
||||||
|
operand commandkit.Supplied[string]
|
||||||
|
}
|
||||||
|
var received input
|
||||||
|
var mode commandkit.ReportingMode
|
||||||
|
factories, calls := 0, 0
|
||||||
|
parse := func(s string) (string, error) { return s, nil }
|
||||||
|
cmd := commandkit.NewCommand(commandkit.CommandSpec[input]{Name: "run", NewInput: func() input { factories++; return input{} }, Flags: []commandkit.Flag[input]{
|
||||||
|
commandkit.ValueFlag(commandkit.FlagMeta{Name: "before"}, (*string)(nil), parse, func(i *input, v commandkit.Supplied[string]) { i.before = v }),
|
||||||
|
commandkit.BoolFlag(commandkit.FlagMeta{Name: "debug", Short: "d"}, false, func(i *input, v commandkit.Supplied[bool]) { i.debug = v }),
|
||||||
|
commandkit.ValueFlag(commandkit.FlagMeta{Name: "after"}, (*string)(nil), parse, func(i *input, v commandkit.Supplied[string]) { i.after = v }),
|
||||||
|
}, Arguments: []commandkit.Argument[input]{commandkit.ValueArgument(commandkit.ArgumentMeta{Name: "operand"}, false, parse, func(i *input, v commandkit.Supplied[string]) { i.operand = v })}, Run: func(inv commandkit.Invocation, i input) commandkit.Outcome {
|
||||||
|
calls++
|
||||||
|
received = i
|
||||||
|
mode = inv.ReportingMode()
|
||||||
|
return commandkit.Success()
|
||||||
|
}})
|
||||||
|
app, err := commandkit.New(commandkit.Identity{Name: "example"}, cmd, commandkit.FlagAlias("-x", "--debug"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, tc := range []struct {
|
||||||
|
args []string
|
||||||
|
value, set bool
|
||||||
|
mode commandkit.ReportingMode
|
||||||
|
}{
|
||||||
|
{nil, false, false, commandkit.ReportingModeOrdinary}, {[]string{"--debug"}, true, true, commandkit.ReportingModeDebug}, {[]string{"--debug", "--quiet"}, true, true, commandkit.ReportingModeQuiet}, {[]string{"--quiet", "--debug"}, true, true, commandkit.ReportingModeDebug}, {[]string{"--debug", "--verbose"}, true, true, commandkit.ReportingModeVerbose}, {[]string{"--debug", "--debug=false"}, false, true, commandkit.ReportingModeOrdinary}, {[]string{"--verbose", "--debug", "--debug=false"}, false, true, commandkit.ReportingModeOrdinary}, {[]string{"--debug", "--quiet=false"}, true, true, commandkit.ReportingModeDebug}, {[]string{"--debug", "--progress=false"}, true, true, commandkit.ReportingModeDebug}, {[]string{"-x"}, true, true, commandkit.ReportingModeDebug}, {[]string{"-d=false"}, false, true, commandkit.ReportingModeOrdinary},
|
||||||
|
} {
|
||||||
|
args := append([]string{"run", "--before=one", "--after=two"}, tc.args...)
|
||||||
|
got := app.Execute(context.Background(), args, commandkit.Streams{Out: io.Discard, Err: io.Discard})
|
||||||
|
if got.Kind != commandkit.OutcomeSuccess || received.debug.Value != tc.value || received.debug.Set != tc.set || mode != tc.mode || received.before.Value != "one" || received.after.Value != "two" {
|
||||||
|
t.Fatalf("%v: result=%#v input=%#v mode=%v", tc.args, got, received, mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, value := range []string{"--debug=", "--debug=invalid"} {
|
||||||
|
beforeFactories, beforeCalls := factories, calls
|
||||||
|
got := app.Execute(context.Background(), []string{"run", value}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
|
||||||
|
if got.Kind != commandkit.OutcomeUsage || factories != beforeFactories || calls != beforeCalls {
|
||||||
|
t.Fatalf("%s result=%#v factories=%d calls=%d", value, got, factories, calls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
got := app.Execute(context.Background(), []string{"run", "--before", "--debug"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
|
||||||
|
if got.Kind != commandkit.OutcomeSuccess || received.before.Value != "--debug" || received.debug.Set || mode != commandkit.ReportingModeOrdinary {
|
||||||
|
t.Fatalf("option-looking value: %#v %#v mode=%v", got, received, mode)
|
||||||
|
}
|
||||||
|
got = app.Execute(context.Background(), []string{"run", "--", "--debug"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
|
||||||
|
if got.Kind != commandkit.OutcomeSuccess || received.operand.Value != "--debug" || received.debug.Set || mode != commandkit.ReportingModeOrdinary {
|
||||||
|
t.Fatalf("operand after terminator: %#v %#v mode=%v", got, received, mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user