#
Walkthrough: Parsley Integration with Cobra
This guide demonstrates how to integrate Parsley with Cobra to build a modular and maintainable CLI application. It leverages the cobra-extension package to implement command handlers based on typed command services, making it a natural fit for Parsley’s dependency injection capabilities.
#
Project Structure
The code for this example can be found at examples/integrations/cobra it has the following structure:
cobra
├── cmd
│ └── main.go
├── go.mod
├── go.sum
└── internal
├── commands
│ └── hello_command.go
├── modules
│ ├── cobra_module.go
│ └── services_module.go
└── services
└── greeter.go
#
Main Application
The cmd/main.go
file serves as the application's entry point. Here, the service dependencies are registered using a ServiceRegistry
instance. Dependency registration is organized in ModuleFunc
methods, keeping the main setup clean. Finally, a *charmer.CommandLineApplication
service is resolved, and its Execute method is called to run the application.
package main
import (
"context"
"github.com/matzefriedrich/cobra-extensions/pkg/charmer"
"github.com/matzefriedrich/parsley-docs/examples/integrations/cobra/internal/modules"
"github.com/matzefriedrich/parsley/pkg/registration"
"github.com/matzefriedrich/parsley/pkg/resolving"
)
func main() {
registry := registration.NewServiceRegistry()
_ = registry.RegisterModule(modules.CobraApplicationModule("cobra-app", "A Parsley-enabled Cobra app"))
_ = registry.RegisterModule(modules.CobraApplicationCommandsModule)
_ = registry.RegisterModule(modules.ServicesModule)
resolver := resolving.NewResolver(registry)
app, _ := resolving.ResolveRequiredService[*charmer.CommandLineApplication](resolver, resolving.NewScopedContext(context.Background()))
err := app.Execute()
if err != nil {
panic(err)
}
}
#
Modules
The internal/modules
package defines the service configurations required for the CLI setup.
The cobra_module.go
module configures the Cobra application and registers it as a singleton service. It also enables the resolution of all *cobra.Command
types as a list, which is necessary to populate the Cobra application with registered commands.
package modules
import (
"context"
"github.com/matzefriedrich/cobra-extensions/pkg/charmer"
"github.com/matzefriedrich/parsley-docs/examples/integrations/cobra/internal/commands"
"github.com/matzefriedrich/parsley/pkg/features"
"github.com/matzefriedrich/parsley/pkg/registration"
"github.com/matzefriedrich/parsley/pkg/resolving"
"github.com/matzefriedrich/parsley/pkg/types"
"github.com/spf13/cobra"
)
// CobraApplicationModule registers a Cobra-based command-line application with the given name and description in the service registry.
func CobraApplicationModule(appName string, appDescription string) func(registry types.ServiceRegistry) error {
return func(registry types.ServiceRegistry) error {
// Enable resolution of *cobra.Command objects at once (as a list)
_ = features.RegisterList[*cobra.Command](registry)
// Register a factory function for *CommandLineApplication
_ = registration.RegisterSingleton(registry, func(resolver types.Resolver) *charmer.CommandLineApplication {
application := charmer.NewCommandLineApplication(appName, appDescription)
// Resolve all command handlers and add them to the app instance
typeCommandServices, _ := resolving.ResolveRequiredServices[*cobra.Command](resolver, context.Background())
for _, command := range typeCommandServices {
application.AddCommand(command)
}
return application
})
return nil
}
}
// CobraApplicationCommandsModule registers typed command handler services in the given service registry.
func CobraApplicationCommandsModule(registry types.ServiceRegistry) error {
_ = registration.RegisterTransient(registry, commands.NewHelloCommand)
return nil
}
The services_module.go
registers application-specific services, such as the Greeter
service.
package modules
import (
"github.com/matzefriedrich/parsley-docs/examples/integrations/cobra/internal/services"
"github.com/matzefriedrich/parsley/pkg/registration"
"github.com/matzefriedrich/parsley/pkg/types"
)
// ServicesModule registers a transient service for creating new instances of Greeter in the provided service registry.
func ServicesModule(registry types.ServiceRegistry) error {
_ = registration.RegisterTransient(registry, services.NewGreeter)
return nil
}
#
Services
The Greeter service in internal/services/greeter.go
generates a greeting message based on the provided name and politeness level. This service is used by command handlers to produce responses.
package services
import "fmt"
type Greeter interface {
SayHello(name string, polite bool) string
}
type greeter struct {
}
func (g *greeter) SayHello(name string, polite bool) string {
if polite {
return fmt.Sprintf("Good day, %s!\n", name)
} else {
return fmt.Sprintf("Hi, %s\n", name)
}
}
func NewGreeter() Greeter {
return &greeter{}
}
#
Command Handlers
Command handlers in this example are services that implement the TypedCommand
interface, providing structured logic and dependency injection. For instance, the helloCommand
service uses Greeter
to output personalized greetings. With cobra-extensions and Parsley, command declaration, service resolution, and command logic are consolidated in the same module, making the codebase easier to navigate and maintain.
package commands
import (
"fmt"
"github.com/matzefriedrich/cobra-extensions/pkg/commands"
"github.com/matzefriedrich/cobra-extensions/pkg/types"
"github.com/matzefriedrich/parsley-docs/examples/integrations/cobra/internal/services"
"github.com/spf13/cobra"
)
type helloCommand struct {
use types.CommandName `flag:"hello" short:"Prints a greeting to the specified name."`
Name string `flag:"name" usage:"The name to greet." required:"true"`
Polite bool `flag:"polite"`
greeter services.Greeter
}
func (h *helloCommand) Execute() {
message := h.greeter.SayHello(h.Name, h.Polite)
fmt.Println(message)
}
var _ types.TypedCommand = (*helloCommand)(nil)
func NewHelloCommand(greeter services.Greeter) *cobra.Command {
command := &helloCommand{
greeter: greeter,
}
return commands.CreateTypedCommand(command)
}
#
Running the Application
As configured in the code, the application will start a Cobra application and call it´s Execute
method. To run the application, navigate to the root directory and execute the following command:
go run main.go hello --name John --polite
This should return:
Good day, John!
For more details on Parsley, check out the Parsley GitHub repository.