Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,16 @@ $(GOLANGCI_LINT): $(LOCALBIN)
test -s $(LOCALBIN)/golangci-lint || curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(LOCALBIN) $(GOLANGCI_LINT_VERSION)


# Format code
# Go module directories (each has its own go.mod)
GO_MODULES := . valkey examples examples/uber_demo

# Format code and tidy all modules
fmt:
gofmt -s -w .
@for dir in $(GO_MODULES); do \
echo "go mod tidy: $$dir"; \
(cd $$dir && go mod tidy) || exit 1; \
done

# Run all checks
check: fmt lint test
Expand Down
36 changes: 36 additions & 0 deletions cobra_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ import (
// package) to avoid a clicky -> clicky/rpc import cycle.
type ContextDataFunc = func(ctx context.Context, flags map[string]string, args []string) (any, error)

// ContextLookupFunc is the context-aware variant of a filter lookup closure.
// When present, the RPC executor prefers it over LookupFunc and feeds the
// request's context.Context (r.Context() over HTTP, cmd.Context() on the CLI)
// so a filter's Options/OptionsWithQuery can resolve request-scoped state
// (e.g. the per-request database handle) instead of a process global.
type ContextLookupFunc = func(ctx context.Context, flags map[string]string, args []string) (any, error)

// dataFuncRegistry maps cobra commands created by AddCommand to closures
// that can invoke the user function directly with flag values.
var dataFuncRegistry sync.Map // map[*cobra.Command]func(flags map[string]string, args []string) (any, error)
Expand All @@ -33,6 +40,11 @@ var contextDataFuncRegistry sync.Map // map[*cobra.Command]ContextDataFunc
// lookupFuncRegistry maps cobra commands to filter metadata lookup closures.
var lookupFuncRegistry sync.Map // map[*cobra.Command]func(flags map[string]string, args []string) (any, error)

// contextLookupFuncRegistry maps cobra commands to context-aware filter
// lookup closures. When present, the RPC converter wires it onto
// RPCOperation.ContextLookupFunc and the executor prefers it over LookupFunc.
var contextLookupFuncRegistry sync.Map // map[*cobra.Command]ContextLookupFunc

// GetDataFunc returns the direct data function registered for a command, if any.
// Used by the RPC converter to wire DataFunc on RPCOperation.
func GetDataFunc(cmd *cobra.Command) func(flags map[string]string, args []string) (any, error) {
Expand All @@ -59,6 +71,15 @@ func GetLookupFunc(cmd *cobra.Command) func(flags map[string]string, args []stri
return nil
}

// GetContextLookupFunc returns the context-aware lookup function registered for
// a command, if any. Used by the RPC converter to wire ContextLookupFunc.
func GetContextLookupFunc(cmd *cobra.Command) ContextLookupFunc {
if v, ok := contextLookupFuncRegistry.Load(cmd); ok {
return v.(ContextLookupFunc)
}
return nil
}

// AddCommand creates a Cobra command with automatic flag parsing from struct tags,
// execution, and result formatting.
//
Expand Down Expand Up @@ -130,6 +151,20 @@ func AddNamedCommand[T any, R any](name string, parent *cobra.Command, opts T, f
return addNamedCommand(name, parent, opts, fn, nil)
}

// AddCommandWithContext is AddCommand whose closure receives the request-scoped
// context (cmd.Context() on the CLI, r.Context() over HTTP). It derives the
// command name from the opts struct exactly like AddCommand, then delegates to
// AddNamedCommandWithContext so the subcommand can resolve a per-request
// database/config instead of a process global.
func AddCommandWithContext[T any, R any](parent *cobra.Command, opts T, fn func(ctx context.Context, opts T) (R, error)) *cobra.Command {
optsType := reflect.TypeOf(opts)
if optsType.Kind() != reflect.Struct {
panic("AddCommandWithContext requires a struct type for opts parameter")
}
name := lo.KebabCase(strings.TrimSuffix(optsType.Name(), "Options"))
return AddNamedCommandWithContext(name, parent, opts, fn)
}

// AddNamedCommandWithContext is AddNamedCommand whose closure receives the
// request-scoped context (cmd.Context() on the CLI, r.Context() over HTTP), so
// the subcommand can resolve a per-request database/config instead of a process
Expand Down Expand Up @@ -179,6 +214,7 @@ func addNamedCommand[T any, R any](
}
if len(subFilters) > 0 {
lookupFuncRegistry.Store(cmd, buildLookupFunc[T](subFilters))
contextLookupFuncRegistry.Store(cmd, buildLookupFuncWithContext[T](subFilters))
}

if namer, ok := optsValue.Interface().(Name); ok {
Expand Down
8 changes: 4 additions & 4 deletions e2e_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ var other = api.Text{Content: "other"}
})
cmd := exec.Command(binaryPath, "lint", "--no-color", "--summary-limit", "1", ".")
cmd.Dir = dir
cmd.Env = append(clickyTestEnv(true), "GOWORK=off")
cmd.Env = append(clickyTestEnv(true), "GOWORK=off", "GOFLAGS=-mod=mod")

var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
Expand Down Expand Up @@ -262,7 +262,7 @@ func (s Server) Pretty() api.Text {
})
cmd := exec.Command(binaryPath, "lint", "--no-color", ".")
cmd.Dir = dir
cmd.Env = append(clickyTestEnv(true), "GOWORK=off")
cmd.Env = append(clickyTestEnv(true), "GOWORK=off", "GOFLAGS=-mod=mod")

var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
Expand All @@ -286,7 +286,7 @@ var direct = api.Text{Content: "bad"}
})
cmd := exec.Command(binaryPath, "lint", "-json", ".")
cmd.Dir = dir
cmd.Env = append(clickyTestEnv(true), "GOWORK=off")
cmd.Env = append(clickyTestEnv(true), "GOWORK=off", "GOFLAGS=-mod=mod")

var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
Expand All @@ -310,7 +310,7 @@ var direct = api.Text{Content: "bad"}
})
cmd := exec.Command(binaryPath, "lint", "--format", "json", ".")
cmd.Dir = dir
cmd.Env = append(clickyTestEnv(true), "GOWORK=off")
cmd.Env = append(clickyTestEnv(true), "GOWORK=off", "GOFLAGS=-mod=mod")

var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
Expand Down
Loading
Loading