couleuvre

package module
v0.15.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 28, 2025 License: GPL-2.0 Imports: 5 Imported by: 2

README

vlbeaudoin/couleuvre

Go command-line app framework

Project is still experimental, version is <1

Installation

Prerequisites
  • Go 1.21.3+
Add to your project

go get codeberg.org/vlbeaudoin/couleuvre

Usage

Example
type Config struct {
	Port   int
	Server struct {
		Motd string
	}
}

app := couleuvre.App[Config]{
	// Will be capitalized and prepended to flag names, using
	// underscores (_) for separation, for environment variables
	// parsing.
	EnvPrefix: "example",
}

// === Add behaviors, flags and commands after this point ===

if err := app.AddBehaviors(
	couleuvre.AppBehaviorCommandRequired,
	couleuvre.AppBehaviorConfigCommand,
	couleuvre.AppBehaviorConfigEnvDisable,

	// ...

); err != nil {
	log.Fatal(err)
}

// Will be registered for env as EXAMPLE_PORT and for flags as one of:
//   -port
//   --port
flag.IntVar(&app.Config.Port, "port", 8080, "the port")

// Will be registered for env as EXAMPLE_SERVER_MOTD and for flags as one of:
//   -server.motd
//   --server.motd
flag.StringVar(&app.Config.Server.Motd, "server.motd", "hello world!", "server message of the day")

if err := app.AddCommands(
	couleuvre.Command{
		Name:        "server",
		Description: "start server",
		Aliases:     []string{"s"},
		Executer: couleuvre.ExecuterFunc(func(args ...string) error {
			log.Println("Starting server...")

			// ...

			return nil
		})},
); err != nil {
	log.Fatal(err)
}

// === Add behaviors, flags and commands before this point ===

if err := app.Parse(); err != nil {
	log.Fatal(err)
}

if err := app.Execute(flag.Args()...); err != nil {
	log.Fatal(err)
}

Design considerations

The following are design considerations that are to be followed as best as possible while developping this framework. They are opinions only.

  • Struct tags are unreliable since they require external tooling to validate. They should be supported but not relied on.
  • External dependencies are to be used sparingly. Ideally, as much of the framework as possible would rely on the standard library.

These considerations may evolve as developement continues, but should solidify as v1 rolls around (note: no timeline has been defined).

Documentation

Acknowledgements

This project is possible due to the go standard library and the https://github.com/goccy/go-yaml parser.

Inspired by:

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrCommandNameDuplicated = fmt.Errorf("command name duplicated")
View Source
var ErrCommandNotFound = fmt.Errorf("command not found")
View Source
var ErrCommandRequired = fmt.Errorf("command required")
View Source
var ErrCommandUnnamed = fmt.Errorf("command cannot be unnamed")
View Source
var ErrExecuterNilButRequired = fmt.Errorf("executer nil but required")
View Source
var ErrNoEnvPrefix = fmt.Errorf("app requires env parsing but no [App.EnvPrefix] was provided")
View Source
var ErrNotImplemented = fmt.Errorf("not implemented")
View Source
var ErrNothingToExecute = fmt.Errorf("nothing to execute")
View Source
var ErrSubcommandRequired = fmt.Errorf("subcommand required")

Functions

func DefaultUsage added in v0.12.0

func DefaultUsage(c Command)

DefaultUsage prints a help message from a command, its subcommands and the global flags

func Execute added in v0.12.0

func Execute(executer Executer, args ...string) error

Execute sends args to provided non-nil Executer

func ExecuteFunc added in v0.12.0

func ExecuteFunc(executerFunc ExecuterFunc, args ...string) error

ExecuteFunc wraps ExecuterFunc.Execute

func FlagNameToEnv added in v0.14.2

func FlagNameToEnv(f *flag.Flag, EnvPrefix string) string

FlagNameToEnv parses flag name and EnvPrefix for a corresponding env var

ToUpper, separate EnvPrefix with _, replace all . and - by _

func ParseAndExecuteSubcommand added in v0.12.0

func ParseAndExecuteSubcommand(command Command, cmd string, args ...string) error

ParseAndExecuteSubcommand executes a parsed subcommand from a provided Command and args

func PrintValidCommands added in v0.12.0

func PrintValidCommands(commands []Command)

PrintValidCommands prints provided the Command slice

func PrintValidEnvVars added in v0.14.1

func PrintValidEnvVars(EnvPrefix string)

Types

type App added in v0.3.0

type App[T Config] struct {

	// App-wide config file
	Config T

	// String to use as prefix for environment variables parsing.
	//
	// Required for environment variables parsing.
	//
	// Will be capitalized and separated using an underscore (_) from the
	// flag name, and any struct separation will add another underscore.
	//
	// ex.: if set to "couleuvrectl", a "Server.Port" flag would parse the
	//      env var "COULEUVRECTL_SERVER_PORT".
	EnvPrefix string

	// Executer for command without subcommands
	Executer Executer
	// contains filtered or unexported fields
}

App is the main abstraction for a command-line application managed by couleuvre

It uses generics to know ahead of time how the app config is formed, to easily manage application config using env, flags and a config file.

It contains a slice of AppBehavior to customize the app, along with a slice of Command to parse arguments through for application subcommands

Example
package main

import (
	"flag"
	"log"

	"codeberg.org/vlbeaudoin/couleuvre"
)

func main() {
	type Config struct {
		Port   int
		Server struct {
			Motd string
		}
	}

	app := couleuvre.App[Config]{
		// Will be capitalized and prepended to flag names, using
		// underscores (_) for separation, for environment variables
		// parsing.
		EnvPrefix: "example",
	}

	// === Add behaviors, flags and commands after this point ===

	if err := app.AddBehaviors(
		couleuvre.AppBehaviorCommandRequired,
		couleuvre.AppBehaviorConfigCommand,
		couleuvre.AppBehaviorConfigEnvDisable,

		// ...

	); err != nil {
		log.Fatal(err)
	}

	// Will be registered for env as EXAMPLE_PORT and for flags as one of:
	//   -port
	//   --port
	flag.IntVar(&app.Config.Port, "port", 8080, "the port")

	// Will be registered for env as EXAMPLE_SERVER_MOTD and for flags as one of:
	//   -server.motd
	//   --server.motd
	flag.StringVar(&app.Config.Server.Motd, "server.motd", "hello world!", "server message of the day")

	if err := app.AddCommands(
		couleuvre.Command{
			Name:        "server",
			Description: "start server",
			Aliases:     []string{"s"},
			Executer: couleuvre.ExecuterFunc(func(args ...string) error {
				log.Println("Starting server...")

				// ...

				return nil
			})},
	); err != nil {
		log.Fatal(err)
	}

	// === Add behaviors, flags and commands before this point ===

	if err := app.Parse(); err != nil {
		log.Fatal(err)
	}

	if err := app.Execute(flag.Args()...); err != nil {
		log.Fatal(err)
	}
}

func (*App[T]) AddBehavior added in v0.12.0

func (a *App[T]) AddBehavior(appBehavior AppBehavior, behaviorArgs ...string) error

AddBehavior adds an AppBehavior to a [App.AppBehavior], adding any provided behaviorArgs as map value

func (*App[T]) AddBehaviors added in v0.12.0

func (a *App[T]) AddBehaviors(appBehaviors ...AppBehavior) error

AddBehaviors adds one or more AppBehavior to a [App.Behaviors]

If you want to use BehaviorArgs, please use App.AddBehaviorsWithArgs instead

func (*App[T]) AddBehaviorsWithArgs added in v0.14.0

func (a *App[T]) AddBehaviorsWithArgs(appBehaviors map[AppBehavior][]string) error

AddBehaviors adds one or more AppBehavior to a [App.Behaviors] with BehaviorArgs

Provided map will be explored and merged into [App.behaviors]

func (*App[T]) AddCommands added in v0.5.0

func (a *App[T]) AddCommands(commands ...Command) error

AddCommands adds one or more Command to the App or returns an error

func (App[T]) BehaviorArgs added in v0.14.0

func (a App[T]) BehaviorArgs(appBehavior AppBehavior) []string

BehaviorArgs returns the args set to the provided AppBehavior, if it was registered in the [App.Behaviors]

func (App[T]) Commands added in v0.5.0

func (a App[T]) Commands() []Command

Commands returns a copy of the commands added to the app

func (App[T]) ContainsBehavior added in v0.12.0

func (a App[T]) ContainsBehavior(appBehavior AppBehavior) bool

ContainsBehavior checks whether [App.Behaviors] contains the specified AppBehavior and discards the value

func (App[T]) DefaultUsage added in v0.12.0

func (a App[T]) DefaultUsage()

DefaultUsage prints a help message from an App, its flags and subcommands

func (App[T]) Execute added in v0.12.0

func (a App[T]) Execute(args ...string) error

Execute is the main entrypoint to an app's execution

It parses through the args and behaviors to execute the proper command or subcommand of the app.

It is necessary to have executed App.Parse prior to using App.Execute.

func (App[T]) GetBehavior added in v0.13.0

func (a App[T]) GetBehavior(appBehavior AppBehavior) (behaviorArgs []string, isRegistered bool)

GetBehavior matches a AppBehavior returning any behaviorArgs and whether it was registered or not

func (App[T]) NCommands added in v0.12.0

func (a App[T]) NCommands() int

NCommands returns the Command slice length currently registered in the App

func (*App[T]) Parse added in v0.3.0

func (a *App[T]) Parse() error

Parse parses the config from the environment into [App.Config]

Must be run at least once, after all flags have been defined, to prepare for command execution.

Config values are obtained from the environment, the config file and command-line arguments.

func (App[T]) ParseAndExecuteCommand added in v0.12.0

func (a App[T]) ParseAndExecuteCommand(cmd string, args ...string) error

ParseAndExecuteCommand parses a string for a matching command in the App and executes it.

It runs App.Parse, then searches App.Commands for a Command matching the provided string executing it with the provided args or returning an error.

func (App[T]) ParseArgsForSubcommand added in v0.12.0

func (a App[T]) ParseArgsForSubcommand(args ...string) (Command, []string, error)

ParseArgsForSubcommand is a wrapper around the function of the same name, see ParseArgsForSubcommand

func (App[T]) ParseCommand added in v0.6.0

func (a App[T]) ParseCommand(cmd string) (found Command, err error)

ParseCommand searches App.Commands for one matching the provided string, returning the found Command or an error.

func (*App[T]) ParseConfig added in v0.12.0

func (a *App[T]) ParseConfig() error

ParseConfig parses the environment, flags and config file for config options

Priorities: config < env < flags

func (App[T]) PrintValidCommands added in v0.12.0

func (a App[T]) PrintValidCommands()

PrintValidCommands prints the Command slice available to the App

type AppBehavior added in v0.12.0

type AppBehavior string

AppBehavior defines an app's behavior when executed

const AppBehaviorCommandRequired AppBehavior = "AppBehaviorCommandRequired"

AppBehaviorCommandRequired causes an error if a command is required but not provided

const AppBehaviorConfigCommand AppBehavior = "AppBehaviorConfigCommand"

AppBehaviorConfigCommand adds a config command to the app

The config command prints the parsed config to stdout.

const AppBehaviorConfigEnvDisable AppBehavior = "AppBehaviorConfigEnvDisable"

AppBehaviorConfigEnvDisable disables environment variables parsing for [App.Config]

const AppBehaviorConfigFileDisable AppBehavior = "AppBehaviorConfigFileDisable"

AppBehaviorConfigFileDisable disables config file parsing for [App.Config]

const AppBehaviorConfigFlagDisable AppBehavior = "AppBehaviorConfigFlagDisable"

AppBehaviorConfigFlagDisable disables top priority flag parsing for [App.Config]

This behavior needs more testing but probably won't behave as expected

const AppBehaviorPrintArgs AppBehavior = "AppBehaviorPrintArgs"

AppBehaviorPrintArgs prints the args received by App.Execute before parsing them into the App

type Command added in v0.2.0

type Command struct {
	// Aliases are short- or long-form alternatives to the [Command.Name]
	Aliases []string

	// Description is used in [DefaultUsage] for describing the [Command]
	Description string

	// Executer is a handler for the execution of a [Command].
	//
	// Other frameworks call this Run or Execute, but the handler form inspired
	// by [http.Handler] and the associated [ExecuterFunc] felt like a good fit
	// for a command line app framework
	Executer Executer

	// Name is the actual command name used for parsing
	//
	// [Command.Aliases] can also be defined for parsing alternatives
	Name string

	// Subcommands holds a [Command] slice for nested subcommands
	Subcommands []Command
	// contains filtered or unexported fields
}

Command is the abstraction for a node in the parsing and execution path

A command is considered complete if it has at least a [Command.Name], but more information gives a more complete experience.

func ParseArgsForSubcommand added in v0.12.0

func ParseArgsForSubcommand(commands []Command, args ...string) (Command, []string, error)

ParseArgsForSubcommand parses the provided args cyclically through the Command slice and any associated subcommand, returning any error OR the final parsed Command and any remaining args.

func ParseCommand added in v0.2.0

func ParseCommand(cmds []Command, cmd string) (found Command, err error)

ParseCommand attempts to match a string with [Command.Name] or [Command.Aliases]

func ParseSubcommand added in v0.12.0

func ParseSubcommand(command Command, cmd string) (Command, error)

ParseSubcommand is a wrapper around Command.ParseSubcommand

func (*Command) AddBehavior added in v0.12.0

func (c *Command) AddBehavior(commandBehavior CommandBehavior, behaviorArgs ...string) error

AddBehavior adds a CommandBehavior to a [Command.Behaviors], adding any provided behaviorArgs as map value

func (*Command) AddBehaviors added in v0.12.0

func (c *Command) AddBehaviors(commandBehaviors ...CommandBehavior) error

AddBehaviors adds one or more CommandBehavior to a [Command.Behaviors]

If you want to use BehaviorArgs, please use Command.AddBehaviorsWithArgs instead

func (*Command) AddBehaviorsWithArgs added in v0.14.0

func (c *Command) AddBehaviorsWithArgs(commandBehaviors map[CommandBehavior][]string) error

AddBehaviors adds one or more CommandBehavior to a [Command.Behaviors] with BehaviorArgs

Provided map will be explored and merged into [Command.behaviors]

func (*Command) AddSubcommands added in v0.12.0

func (c *Command) AddSubcommands(commands ...Command) error

AddSubcommands adds one or more Command into [Command.Subcommands}

An error is returned if a [Command.Name] is empty, or if a command name is already taken.

TODO validate aliases are not overlapping (if it's not too much a burden on the build/execution time)

func (Command) BehaviorArgs added in v0.14.0

func (c Command) BehaviorArgs(commandBehavior CommandBehavior) []string

BehaviorArgs returns the args set to the provided CommandBehavior, if it was registered in the [Command.Behaviors]

func (Command) Complete added in v0.5.0

func (c Command) Complete() bool

Complete returns whether a Command is complete

It only really checks that the name is non-empty

func (Command) ContainsBehavior added in v0.12.0

func (c Command) ContainsBehavior(commandBehavior CommandBehavior) bool

ContainsBehavior checks whether [Command.Behaviors] contains the specified CommandBehavior

func (Command) Execute added in v0.7.0

func (c Command) Execute(args ...string) error

Execute starts the parsing/execution cycle for a command

Typically, you would run App.Execute instead but this is kept as a manual way to execute a Command, and to satisfy the Executer interface, allowing a Command to be the [Executer] of another Command

func (Command) GetBehavior added in v0.13.0

func (c Command) GetBehavior(commandBehavior CommandBehavior) (behaviorArgs []string, isRegistered bool)

GetBehavior matches a CommandBehavior returning any behaviorArgs and whether it was registered or not

func (Command) NSubcommands added in v0.12.0

func (c Command) NSubcommands() int

NSubcommands returns the amount of subcommands registered to a Command

func (Command) ParseAndExecuteSubcommand added in v0.12.0

func (c Command) ParseAndExecuteSubcommand(cmd string, args ...string) error

ParseAndExecuteSubcommand executes any parsed subcommand from Command.ParseSubcommand and args

func (Command) ParseSubcommand added in v0.12.0

func (c Command) ParseSubcommand(cmd string) (Command, error)

ParseSubcommand is a wrapper around ParseCommand parsing through [Command.Subcommands]

func (Command) String added in v0.2.0

func (c Command) String() string

String returns a Command name for string formatting

type CommandBehavior added in v0.12.0

type CommandBehavior string

CommandBehavior defines a command's behavior when executed

const CommandBehaviorSubcommandRequired CommandBehavior = "CommandBehaviorSubcommandRequired"

CommandBehaviorSubcommandRequired causes an error if a subcommand is required but not provided to a Command.Execute

type Config added in v0.9.0

type Config interface{}

type Executer added in v0.12.0

type Executer interface {
	Execute(args ...string) error
}

Executer defines any object capable of executing itself with args, returning any error

It is used by App and Command as their actual execution functions

type ExecuterFunc added in v0.7.0

type ExecuterFunc func(args ...string) error

ExecuterFunc allows wrapping any `func(args ...string) error` to satisfy the Executer interface

func (ExecuterFunc) Execute added in v0.12.0

func (f ExecuterFunc) Execute(args ...string) error

Execute executes the ExecuterFunc by giving it the provided args

It makes ExecuterFunc satisfy the Executer interface

Directories

Path Synopsis
cmd
couleuvrectl command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL