greenery

package module
v0.9.1 Latest Latest
Warning

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

Go to latest
Published: Jul 2, 2018 License: BSD-2-Clause Imports: 31 Imported by: 0

README

Build Status Coverage status GoDoc

NOTE

Greenery has not been officially released yet, it is undergoing final polishing before the 1.0 release, this means that any interfaces are still subject to change. Please do feel free to open issues if you think anything in the library would benefit from changing, especially in terms of interfaces and functionality.

Overview

Greenery is a framework that can be used to create localized CLI applications supporting command-line, environment and configuration-file options.

It is an opinionated porcelain built on top of Cobra and Viper. In greenery, rather than via code, the configuration variables are defined in a single configuration structure, which is mapped to the user-visible variables via golang struct annotations.

User commands are invoked with a configuration structure set to the currently effective configuration, taking into account the command line, the environment and any specified configuration files.

A localizeable documentation functionality is also provided, together with some predefined commands that can be used to generate configuration files that can be used by the application itself.

Table of Contents

Installing

To install greenery simply run:

go get -u github.com/woodensquares/greenery/...

to install both the library, its test helper and zap logging back-end additional functionality. After this you are ready to go.

Concepts

Greenery operates in the following manner: given a command line with flags and arguments, the environment and optionally a TOML configuration file, it will take all supported ways to specify flags, convert those in configuration struct values, and pass this effective configuration, together with any command line arguments, to the function implementing the command requested by the user.

Configuration

The configuration struct, embedding the base configuration provided by the greenery library, is what should control the execution of the program. Handler functions will have access to the effective configuration.

Commands

Commands are the commands that the user will choose on the command line to represent an action they want to execute, which will be mapped to a handler implementing it.

Commands in greenery are referenced both by the config struct annotation, which controls the binding between commands and flags, by the map containing information about which command calls which handler function, and the documentation passed to the library.

Command names must not contain spaces or any of the & , | < characters. The < character is allowed as the last character of the command name, to signify this command will take arguments, see below for more information.

The > character is used to define subcommand relationships, so a command named "hat>fedora" in a "make" program, would map to a "make hat fedora" command line (where there could be a separate "hat>baseball" command defined, mapping to "make hat baseball").

Flags

Flags are options that affect user commands, in greenery flags are values that must fulfill the flag.Value interface. In order for greenery to operate correctly in terms of creating and parsing the configuration for non-standard types, the encoding.TextMarshaler and encoding.TextUnmarshaler interfaces are also taken into account respectively when generating configuration files, and parsing configuration/environment values.

Arguments

Arguments are additional arguments the user will put on the command line after the specified command, if a command is meant to support arguments it should be declared with a < character.

Examples

These examples are also included in the godoc documentation, linked above, which contains additional examples.

A minimal example

The minimal example, as its name implies, shows how to use the greenery library to create probably the simplest application possible, with one command and one flag.

package main

import (
    "fmt"
    "io/ioutil"
    "os"

    "github.com/woodensquares/greenery"
)

After the preamble let's create the configuration for an application that we assume will execute some REST GET request against a server, in this case we'd like the application to allow us to specify a URI to connect to, and to have a timeout for the request.

The application will have the URI as a command line parameter, and will have the timeout as an option that can be set via the commandline, environment or configuration file.

type exampleMinimalConfig struct {
    *greenery.BaseConfig
    Timeout *greenery.IntValue `greenery:"get|timeout|t,      minimal-app.timeout,   TIMEOUT"`
}

our configuration struct embeds the BaseConfig struct available in the library, and adds our parameter. The parameter is defined via a greenery struct annotation in a format described below.

With this definition, our flag will be available as --timeout/-t on the command line for the get parameter, via the MINIMAL_TIMEOUT environmental variable, and the timeout parameter in the configuration file under the [minimal-app] section.

func exampleNewMinimalConfig() *exampleMinimalConfig {
    cfg := &exampleMinimalConfig{
        BaseConfig: greenery.NewBaseConfig("minimal", map[string]greenery.Handler{
            "get<": exampleMinimalGetter,
        }),
        Timeout: greenery.NewIntValue("Timeout", 0, 1000),
    }

    if err := cfg.Timeout.SetInt(400); err != nil {
        panic("Could not initialize the timeout to its default")
    }

    return cfg
}

typically one would want to have a function that creates an initialized configuration struct, with any default values. In our case this function will create a struct with a default timeout value of 400, and set its limits to between 0 and 1000 via the provided IntValue flag.

The base configuration is also initialized by passing "minimal" as our application name, and declaring that we have a "get" command that takes a commandline argument (the "<" character) and will call the exampleMinimalGetter function if invoked.

In order for the library to be able to display help, we should now declare the documentation for our program

var exampleMinimalDocs = map[string]*greenery.DocSet{
    "en": &greenery.DocSet{
        Short: "URL fetcher",
        Usage: map[string]*greenery.CmdHelp{
            "get": &greenery.CmdHelp{
                Use:   "[URI to fetch]",
                Short: "Retrieves the specified page",
            },
        },
        CmdLine: map[string]string{
            "Timeout": "the timeout to use for the fetch",
        },
    },
}

for simplicity's sake we are just providing the English documentation, and we'll be using the struct format for the documentation, rather than the string list format. See later in the document for an example of a string list documentation.

Our application will just have a short help format both for itself and for the get command, it also will reuse the command line help for the config file help, which means we just have to specify CmdLine.

It is now time to create the actual logic for our application, which in this case will simply validate that the user did pass an argument to the command, and will print out what it would be doing with the effective timeout, as well as emitting a debug message.

func exampleMinimalGetter(lcfg greenery.Config, args []string) error {
    cfg := lcfg.(*exampleMinimalConfig)

    if len(args) != 1 {
        return fmt.Errorf("Invalid number of command line arguments")
    }

    cfg.Debugf("fetching %s, timeout %d", args[0], cfg.Timeout.Value)
    fmt.Printf("Will fetch %s with timeout %d milliseconds\n\n", args[0], cfg.Timeout.Value)
    return nil
}

The main function for our application simply creates our configuration, defers a Cleanup call, and executes it.

func main() {
    cfg := exampleNewMinimalConfig()
    defer cfg.Cleanup()

    if err := cfg.Execute(cfg, exampleMinimalDocs); err != nil {
        fmt.Fprintf(os.Stderr, "Error executing: %s\n", err)
        os.Exit(1)
    }
}

Let's now look at how this minimal application works

Invocations

Let's first try to run without passing any parameter

~: go run minimal.go 
URL fetcher

Usage:
  minimal [command]

Available Commands:
  config      Configuration file related commands
  get         Retrieves the specified page
  help        Help about any command
  version     Prints out the version number of the program

Flags:
  -c, --config string      The configuration file location
      --help               help information for the application.
      --log-file string    The log file location
  -l, --log-level string   The log level of the program. Valid values are "error", "warn", "info" and "debug" (default "error")
      --no-cfg             If set no configuration file will be loaded
      --no-env             If set the environment variables will not be considered
      --pretty             If set the console output of the logging calls will be prettified
  -v, --verbosity int      The verbosity of the program, an integer between 0 and 3 inclusive. (default 1)

"minimal [command] --help" provides more information about a command.

As you can see we will be getting the help for the application, with our get command as well as the available default commands and options.

Our typical invocation would be simply to execute the get command

~: go run minimal.go get http://somewhere.com
Will fetch http://somewhere.com with timeout 400 milliseconds

as you can see the timeout used will be our default, this can be changed of course via the commandline

go run minimal.go get --timeout 100 http://somewhere.com
Will fetch http://somewhere.com with timeout 100 milliseconds

or the environment

~: export MINIMAL_TIMEOUT=440
~: go run minimal.go get http://somewhere.com
Will fetch http://somewhere.com with timeout 440 milliseconds

we can also create a configuration file and set it there, let's create a configuration file in our current directory

~: go run minimal.go config init
Configuration file generated at ........../minimal.toml
~: cat minimal.toml 
# Configuration generated on 2018-06-30T14:07:46-07:00

# The log file location
log-file = ""
# The log level of the program. Valid values are "error", "warn", "info" and "debug"
log-level = "error"
# If set the environment variables will not be considered
no-env = false
# If set the console output of the logging calls will be prettified
pretty = false
# The verbosity of the program, an integer between 0 and 3 inclusive.
verbosity = 1

[minimal-app]
# the timeout to use for the fetch
timeout = 400

as you can see default values for all the internal flags, as well as our timeout flag, have been added. If we now edit this file and provide a different value, it will be honored

~: cat minimal.toml | grep timeout
# the timeout to use for the fetch
timeout = 300
~: go run minimal.go get http://somewhere.com
Will fetch http://somewhere.com with timeout 300 milliseconds

the application automatically will look for a configuration file named applicationname.toml in the various XDG configuration directories, as well as in the current directory.

A localized example

The localized example is meant to be exactly the same as the minimal example, but showing how it could be localized. For fun it is showing a pig latin localization, that can be seen by setting LANG to zz.ZZZ

This sample, unlike the minimal sample above, shows the "list of strings" way of specifying a documentation set, this can be useful if non-developers will be in charge of the documentation directly, for example the default language documentation rather than the struct above will be

var exampleLocalizedEnglish = []string{
	"1.0",
	greenery.DocBaseDelimiter,

	// Root command
	greenery.DocShort,
	"URL fetcher",
	greenery.DocExample,
	"    See the get command for examples",
	greenery.DocBaseDelimiter,

	// Additional commands, get in this case
	greenery.DocUsageDelimiter,
	"get",
	"[URI to fetch]",
	"Retrieves the specified page",
	"",
	`    To retrieve a page available on localhost at /hello.html simply run

    localized get http://localhost/hello.html`,
	greenery.DocUsageDelimiter,

	// Cmdline flag documentation
	greenery.DocCmdlineDelimiter,
	"Timeout",
	"the timeout, in milliseconds, to use for the fetch",
	greenery.DocCmdlineDelimiter,

	// Custom message
	greenery.DocCustomDelimiter,
	"Message",
	"Will fetch %s with timeout %d milliseconds\n",
	greenery.DocCustomDelimiter,
}

note in this case we are also showing that custom strings can be added to the documentation for localization of code-emitted messages, the getter function in fact in this sample has the following printf call

func exampleLocalizedGetter(lcfg greenery.Config, args []string) error {

    // ...

    fmt.Printf(docs.Custom["Message"], args[0], cfg.Timeout.Value)
	return nil
}

the largest section of this file is the pig latin localization, once these two localizations are created, they can be converted to the usual struct format via

var exampleLocalizedDocs = greenery.ConvertOrPanic(map[string][]string{
	"en":     exampleLocalizedEnglish,
	"zz.ZZZ": exampleLocalizedPiglatin,
})

in this case we are using the ConvertOrPanic function, which is typically used for package variable definitions.

Reference

The annotation format

As shown above in the sample code, greenery operates by mapping exported struct values to config/environment/commandline flags.

Each exported struct member in the config struct is expected to contain a greenery annotation, unexported members will be ignored, and it is not legal to override any of the default base variables in the embedding struct: this will generate an error.

Greenery struct tags are composed of three parts separated by commas

    `greenery:"get|timeout|t,      minimal-app.timeout,   TIMEOUT"`
Commandline

The first part of the annotation controls the commandline behavior of the flag, it is composed by three parts separated by pipe characters: the first part is the command this flag belongs to, the second is the long name of the option, and the third is a single letter used as a shortname.

If the long or short name for the options are not present, they will not be available. If the command name is not present, in general the flag is configuration/environment only, in that case the first part should be specified as ||none or ||custom depending if the flag in question requires custon TOML deserialization.

Config file

The second part of the annotation controls the name of the option in the configuration file, it is composed by two parts separated by a period. The first part is the header of the section this option belongs to, in this case a section named [minimal-app], while the second is the name of the configuration file variable corresponding to the flag.

If this part of the annotation is not present, the flag is not going to be accessible via the configuration file. If the section name is not present, the flag will be part of the base section of the configuration file together with the other base flags like log level and so on.

Environment

The third part of the annotation controls the name of the environmental variable corresponding to the option. It will be accessible on the environment via NAMEOFTHEAPP_[name] where NAMEOFTHEAPP is the uppercased name of the application as specified in the greenery.NewBaseConfig call.

If this part of the annotation is not present, the flag is not going to be available via the environment.

Precedence

The precedence of flags is command line overrides environment overrides configuration file.

Localization

The default language for the library is "en" but can be set in BaseConfigOptions with the version strings, by default the library is currently providing "en" and "it" translations for the default commands.

In order to simplify localization efforts by non-coders, besides writing the wanted help documentation in a DocSet struct format, it is possible to write it as a list of strings with identifiers, this should hopefully enable easy editing by non-go-developers who can be simply told they should change only text between double quotes.

The format is as follows

Section Delimiter (base, help, cmd, )
Section content
Section Delimiter
...

Each section can appear only once in the list of strings in any order. Each section has a separate set of internal identifiers discussed below. Complete examples are available in the unit tests as well as in doc/internal/ DefaultDocs contains all the supported languages as a map of string lists following this format. As a library user one can decide to use this format, and call greenery.ConvertDocs, or write a DocSet directly.

------ HELP ------
help variable name,
translated help text,
.....
------ USAGE ------
command name, short translated help text, long translated help text
command name, short translated,long translated
...
------ COMMANDLINE ------
config struct variable name, translated variable commandline help text
.....
------ CONFIG ------
config block, config block help
.....
config struct variable name, translated variable config file help text
.....

In order to make it easy to differentiate between library-related strings and user provided strings, constants have been defined containing all the relevant strings mentioned above.

Please see the localized sample code for a complete example that will fully localize any default provided flag or message.

Logging

Logging calls with custom severities and verbosities are supported, by default a very basic stderr logger will service any greenery logging calls, however a higher performance and more full featured layer is provided via greenery/zapbackend that will use the excellent https://github.com/uber-go/zap/ logger.

It is possible also to use an arbitrary provided logger to service these calls as long as it implements the provided Logger interface.

Tracing

Tracing, controlled by the --trace / APPNAME_TRACE flags, or programmatical Start/StopTracing calls, is a special very verbose logging level which in addition to any user code tracing calls will be printing a lot of internal greenery tracing information.

Trace cannot be set via config file, only via the environment or the command line. Note that for tracing there is no override, if it's set in the command line OR environment it will be turned on.

Configuration files

Applications created with the greenery library will support configuration files by default, if this is not desired the --no-cfg / APPNAME_NOCFG flags can be used.

Only TOML-formatted files are supported for now, the the application automatically will look for a configuration file named applicationname.toml in the various XDG configuration directories, as well as in the current directory, or in the location specified via the --config / -c flag.

Documentation

Overview

Package greenery is a localizable struct annotation based command-line application framework.

Greenery can be used to create localized CLI applications supporting command-line, environment and configuration-file options.

It is an opinionated porcelain built on top of Cobra (https://github.com/spf13/cobra) and Viper (https://github.com/spf13/viper). In greenery, rather than via code, the configuration variables are defined in a single configuration structure, which is mapped to the user-visible variables via golang struct annotations.

User commands are invoked with a configuration structure set to the currently effective configuration, taking into account the command line, the environment and any specified configuration files.

A localizeable documentation functionality is also provided, together with some predefined commands that can be used to generate configuration files that can be used by the application itself.

See https://github.com/woodensquares/greenery for additional usage information.

Example (Custom)

Example_custom is an example application showing how to enable greenery to parse custom configuration variables.

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/woodensquares/greenery"
)

type exampleCustomNameValue struct {
	Name  string
	Value string
}

type exampleCSVStruct struct {
	First  string
	Second string
	Third  string
}

type exampleCustomConfig struct {
	*greenery.BaseConfig
	NameValue []exampleCustomNameValue `greenery:"||custom,           custom-app.namevalue,"`
	CSV       exampleCSVStruct         `greenery:"||custom,           custom-app.csv,"`
	List      []string                 `greenery:"||custom,           custom-app.csv,"`
}

func exampleNewCustomConfig() *exampleCustomConfig {
	cfg := &exampleCustomConfig{
		BaseConfig: greenery.NewBaseConfig("custom", map[string]greenery.Handler{
			"display": exampleCustomDisplay,
		}),
		NameValue: []exampleCustomNameValue{},
		CSV:       exampleCSVStruct{},
		List:      []string{},
	}

	cfg.RegisterExtraParse(exampleCustomParse, []string{
		"custom-app.namevalue",
		"custom-app.csv",
		"custom-app.list",
	})

	return cfg
}

var exampleCustomDocs = map[string]*greenery.DocSet{
	"en": &greenery.DocSet{
		Short: "URL fetcher",
		Usage: map[string]*greenery.CmdHelp{
			"display": &greenery.CmdHelp{
				Short: "Show some custom variables",
			},
		},
		ConfigFile: map[string]string{
			"NameValue": "a set of name/value pairs",
			"CSV":       "a comma separated value variable",
			"List":      "a list of strings",
		},
	},
}

func exampleCustomParse(lcfg greenery.Config, vals map[string]interface{}) ([]string, error) {
	cfg := lcfg.(*exampleCustomConfig)
	var processed []string

	for k, v := range vals {
		switch k {
		case "custom-app.namevalue":
			if err := lcfg.Unmarshal(k, &cfg.NameValue); err != nil {
				return nil, err
			}
			processed = append(processed, k)
		case "custom-app.list":
			if err := lcfg.Unmarshal(k, &cfg.List); err != nil {
				return nil, err
			}
			processed = append(processed, k)
		case "custom-app.csv":
			vv, ok := v.(string)
			if !ok {
				return nil, fmt.Errorf("Unexpected type %T for key %s, not a string: %v", v, k, v)
			}

			parts := strings.Split(vv, ",")
			if len(parts) != 3 {
				return nil, fmt.Errorf("Invalid value for key %s: %v", k, v)
			}

			cfg.CSV.First = parts[0]
			cfg.CSV.Second = parts[1]
			cfg.CSV.Third = parts[2]

			processed = append(processed, k)
		}
	}

	return processed, nil
}

func exampleCustomDisplay(lcfg greenery.Config, args []string) error {
	cfg := lcfg.(*exampleCustomConfig)
	fmt.Printf("NameValue is %v\n", cfg.NameValue)
	fmt.Printf("CSV is %v\n", cfg.CSV)
	fmt.Printf("List is %v\n", cfg.List)
	return nil
}

func exampleCustomMain() {
	cfg := exampleNewCustomConfig()
	defer cfg.Cleanup()

	if err := cfg.Execute(cfg, exampleCustomDocs); err != nil {
		fmt.Fprintf(os.Stderr, "Error executing: %s\n", err)
		os.Exit(1)
	}
}

// Example_custom is an example application showing how to enable greenery to
// parse custom configuration variables.
func main() {
	fmt.Println("----------------------------------------------------------")
	cfgFile, err := ioutil.TempFile("", "custom")
	if err != nil {
		fmt.Println("Cannot create a temporary file")
		os.Exit(1)
	}
	defer func() {
		_ = os.Remove(cfgFile.Name())
	}()

	if _, err = cfgFile.Write([]byte(`# Sample config file
[custom-app]
csv = "first,second,third"
list = [ "10.0.0.1", "10.0.0.2", "10.0.0.3" ]

[[custom-app.namevalue]]
name = "k1"
value = "v1"
[[custom-app.namevalue]]
name = "k2"
value = "v2"
`)); err != nil {
		fmt.Println("Cannot write the config file")
		os.Exit(1)
	}
	if err := cfgFile.Close(); err != nil {
		fmt.Println("Cannot close the config file")
		os.Exit(1)
	}

	os.Args = []string{"custom", "-c", cfgFile.Name(), "display"}
	fmt.Printf("Displaying some custom values set in the config file\n\n")
	exampleCustomMain()

}
Output:

----------------------------------------------------------
Displaying some custom values set in the config file

NameValue is [{k1 v1} {k2 v2}]
CSV is {first second third}
List is [10.0.0.1 10.0.0.2 10.0.0.3]
Example (Localized)

Example_localized is just like Example_minimal, but supporting a fully localized additional language, pig latin, if called with LANG set to zz.ZZZ. A sample invocation of the help message setting the LANG environment variable to two different values is provided.

package main

import (
	"fmt"
	"log"
	"os"

	"github.com/woodensquares/greenery"
)

type exampleLocalizedConfig struct {
	*greenery.BaseConfig
	Timeout *greenery.IntValue `greenery:"get|timeout|t,      localized-app.timeout,   TIMEOUT"`
}

func exampleNewLocalizedConfig() *exampleLocalizedConfig {
	cfg := &exampleLocalizedConfig{
		BaseConfig: greenery.NewBaseConfig("localized", map[string]greenery.Handler{
			"get<": exampleLocalizedGetter,
		}),
		Timeout: greenery.NewIntValue("Timeout", 0, 1000),
	}

	if err := cfg.Timeout.SetInt(400); err != nil {
		panic("Could not initialize the timeout to its default")
	}

	return cfg
}

// Sample localization in pig latin, using the []strings format for
// compactness and ease of editing even for non-golang developers
var exampleLocalizedPiglatin = []string{
	"1.0",
	greenery.DocBaseDelimiter,
	// Root command values
	greenery.DocShort,
	"RLUay etcherfay",
	greenery.DocLong,
	"RLUay etcherfay ongerlay escriptionday",
	greenery.DocExample,
	"    Eesay ethay etgay ommandcay orfay examplesay",
	greenery.DocHelpFlag,
	"Elphay informationay orfay ethay applicationay.",
	greenery.DocCmdFlags,
	"[lagsfay]",
	greenery.DocConfigEnvMsg1,
	`Ethay ollowingfay environmentay ariablesvay areay activeay anday ouldcay affectay ethay executionay
ofay ethay ogrampray ependingday onay ommandcay inelay argumentsay:`,
	greenery.DocConfigEnvMsg2,
	"Onay ariablesvay",
	greenery.DocConfigEnvMsg3,
	"Ethay ollowingfay environmentay ariablesvay areay availableay orfay isthay ogrampray:",
	greenery.DocBaseDelimiter,

	// General help template strings
	greenery.DocHelpDelimiter,
	greenery.DocUsage,
	"Sageuay:",
	greenery.DocAliases,
	"Liasesaay:",
	greenery.DocExamples,
	"Ampleseay:",
	greenery.DocAvailableCommands,
	"Vailableaay Ommandscay:",
	greenery.DocFlags,
	"Lagsfay:",
	greenery.DocGlobalFlags,
	"Lobalgay Lagsfay:",
	greenery.DocAdditionalHelpTopics,
	"Dditionalaay elphay opicstay:",
	greenery.DocProvidesMoreInformationAboutACommand,
	"povidespay oremay informationay aboutay aay ommandcay.",
	greenery.DocHelpDelimiter,

	// Individual command usage strings, system first
	greenery.DocUsageDelimiter,
	greenery.DocHelpCmd,
	"[ommandcay]",
	"Elphay aboutay anyay command",
	`Elphay ovidesrpay helpay orfay anyay ommandcay inay ethay applicationay.
Implysay ypetay appnameay elphay [path otay ommandcay] orfay ullfay etailsday.`,
	"",
	greenery.DocConfigCmd,
	"",
	"Onfigurationcay ilefay elatedray ommandscay",
	"",
	"",
	greenery.DocConfigInitCmd,
	"",
	"Reatescay aay efaultday onfigcay ilefay inay wdcay oray hereway -c isay etsay",
	`Enwhay histay ommandcay isay executeday aay onfigurationcay ilefay illway ebay reatedcay
"inay hetay pecifiedsay ocationlay (in hetay urrentcay irectoryday ybay default", oray overnedgay
"ybay hetay aluevay assedpay otay hetay wdcay flag). Histay onfigurationcay ilefay illway ontaincay
"allay hetay upportedsay ariablesvay ithway heirtay efaultday values`,
	"",
	greenery.DocConfigEnvCmd,
	"",
	"Howssay hetay activeay environmentay ariablesvay hattay ouldway impactay hetay program",
	"",
	"",
	greenery.DocConfigDisplayCmd,
	"",
	"Howssay hetay urrentcay onfigurationcay values",
	`Howsay hetay urrentcay onfigurationcay aluesvay akingtay intoay accountay allay environmentay
"anday onfigurationcay ilefay values. Command-line agsflay invaliday orfay hetay onfigcay isplayday
"ommandcay ouldway otnay ebay displayed`,
	"",
	greenery.DocVersionCmd,
	"",
	"Rintspay outay hetay ersionvay umbernay ofay hetay ogramrpay",
	"",
	"",

	// Our command
	"get",
	"[UEI otay fetch]",
	"Etrievesray hetay pecifiedsay agepay",
	"",
	`    Otay etrieveray a agepay availableay onay localhost atay /hello.html implysay unray

    localized get http://localhost/hello.html`,
	greenery.DocUsageDelimiter,

	// Cmdline variable descriptions, system first
	greenery.DocCmdlineDelimiter,
	greenery.DocLogLevel,
	"Hetay oglay evellay ofay hetay ogrampray. Alidvay aluesvay areay \"error\", \"warn\", \"info\" anday \"debug\"",
	greenery.DocConfFile,
	"Hetay onfigurationcay ilefay ocationlay",
	greenery.DocLogFile,
	"Hetay oglay ilefay ocationlay",
	greenery.DocPretty,
	"Fiay etsay hetay onsolecay outputay ofay hetay ogginglay allscay illway ebay ettifiedpray",
	greenery.DocNoCfg,
	"Fiay etsay onay onfigurationay ilefay illway ebay oadedlay",
	greenery.DocNoEnv,
	"Fiay etsay hetay environmentay ariablesvay illway otnay ebay onsideredcay",
	greenery.DocVerbosity,
	"Hetay erbosityvay ofay hetay ogrampray, anay integeray etweenbay 0 anday 3 inclusiveay.",
	greenery.DocDoTrace,
	"Enablesay acingtray",
	greenery.DocCfgLocation,
	"Hereway otay itewray hetay onfigurationcay ilefay, oneay ofay \"cwd\", \"user\" oray \"system\"",
	greenery.DocCfgForce,
	"Fiay specified, anyay existingay onfigurationcay ilesfay illway ebay overwrittenay",
	// our flag
	"Timeout",
	"Hetay imeouttay otay useay orfay hetay ETGay operationay",
	greenery.DocCmdlineDelimiter,

	// Config file variable descriptions (where different from the cmdline)
	greenery.DocConfigDelimiter,
	greenery.DocConfigHeader,
	"Onfigurationcay eneratedgay onay {{ date }}",
	greenery.DocConfigDelimiter,

	// Custom message
	greenery.DocCustomDelimiter,
	"Message",
	"Illway etchfay %s ithway imeouttay %d illisecondsmay\n",
	greenery.DocCustomDelimiter,
}

// Same in English, this will of course contain only values specific to our
// application.
var exampleLocalizedEnglish = []string{
	"1.0",
	greenery.DocBaseDelimiter,

	// Root command
	greenery.DocShort,
	"URL fetcher",
	greenery.DocExample,
	"    See the get command for examples",
	greenery.DocBaseDelimiter,

	// Additional commands, get in this case
	greenery.DocUsageDelimiter,
	"get",
	"[URI to fetch]",
	"Retrieves the specified page",
	"",
	`    To retrieve a page available on localhost at /hello.html simply run

    localized get http://localhost/hello.html`,
	greenery.DocUsageDelimiter,

	// Cmdline flag documentation
	greenery.DocCmdlineDelimiter,
	"Timeout",
	"the timeout, in milliseconds, to use for the fetch",
	greenery.DocCmdlineDelimiter,

	// Custom message
	greenery.DocCustomDelimiter,
	"Message",
	"Will fetch %s with timeout %d milliseconds\n",
	greenery.DocCustomDelimiter,
}

var exampleLocalizedDocs = greenery.ConvertOrPanic(map[string][]string{
	"en":     exampleLocalizedEnglish,
	"zz.ZZZ": exampleLocalizedPiglatin,
})

func exampleLocalizedGetter(lcfg greenery.Config, args []string) error {
	cfg := lcfg.(*exampleLocalizedConfig)

	if len(args) != 1 {
		return fmt.Errorf("Invalid number of command line arguments")
	}

	cfg.Debugf("fetching %s, timeout %d", args[0], cfg.Timeout.Value)
	_, docs := cfg.GetDocs()
	fmt.Printf(docs.Custom["Message"], args[0], cfg.Timeout.Value)
	return nil
}

func exampleLocalizedMain() {
	cfg := exampleNewLocalizedConfig()
	defer cfg.Cleanup()

	cfg.VersionMajor = "1"
	cfg.VersionMinor = "0"

	if err := cfg.Execute(cfg, exampleLocalizedDocs); err != nil {
		fmt.Fprintf(os.Stderr, "Error executing: %s\n", err)
		os.Exit(1)
	}
}

// Example_localized is just like Example_minimal, but supporting a fully
// localized additional language, pig latin, if called with LANG set to
// zz.ZZZ. A sample invocation of the help message setting the LANG
// environment variable to two different values is provided.
func main() {
	fmt.Println("----------------------------------------------------------")
	if err := os.Setenv("LANG", "en_US.UTF-8"); err != nil {
		log.Fatal(err.Error())
	}

	fmt.Printf("Default help for the get command\n\n")
	os.Args = []string{"localized", "get", "-h"}
	exampleLocalizedMain()

	fmt.Printf("Default command output\n\n")
	os.Args = []string{"localized", "get", "http://127.0.0.1"}
	exampleLocalizedMain()

	if err := os.Setenv("LANG", "zz.ZZZ"); err != nil {
		log.Fatal(err.Error())
	}
	fmt.Println("----------------------------------------------------------")
	fmt.Printf("\nLocalized help for the get command\n\n")
	os.Args = []string{"localized", "get", "-h"}
	exampleLocalizedMain()

	fmt.Printf("Localized command output\n\n")
	os.Args = []string{"localized", "get", "http://127.0.0.1"}
	exampleLocalizedMain()

}
Output:

----------------------------------------------------------
Default help for the get command

Retrieves the specified page

Usage:
  localized get [URI to fetch] [flags]

Examples:
    To retrieve a page available on localhost at /hello.html simply run

    localized get http://localhost/hello.html

Flags:
  -t, --timeout int   the timeout, in milliseconds, to use for the fetch (default 400)

Global Flags:
  -c, --config string      The configuration file location
      --help               help information for the application.
      --log-file string    The log file location
  -l, --log-level string   The log level of the program. Valid values are "error", "warn", "info" and "debug" (default "error")
      --no-cfg             If set no configuration file will be loaded
      --no-env             If set the environment variables will not be considered
      --pretty             If set the console output of the logging calls will be prettified
  -v, --verbosity int      The verbosity of the program, an integer between 0 and 3 inclusive. (default 1)

Default command output

Will fetch http://127.0.0.1 with timeout 400 milliseconds
----------------------------------------------------------

Localized help for the get command

Etrievesray hetay pecifiedsay agepay

Sageuay:
  localized get [UEI otay fetch] [lagsfay]

Ampleseay:
    Otay etrieveray a agepay availableay onay localhost atay /hello.html implysay unray

    localized get http://localhost/hello.html

Lagsfay:
  -t, --timeout int   Hetay imeouttay otay useay orfay hetay ETGay operationay (default 400)

Lobalgay Lagsfay:
  -c, --config string      Hetay onfigurationcay ilefay ocationlay
      --help               Elphay informationay orfay ethay applicationay.
      --log-file string    Hetay oglay ilefay ocationlay
  -l, --log-level string   Hetay oglay evellay ofay hetay ogrampray. Alidvay aluesvay areay "error", "warn", "info" anday "debug" (default "error")
      --no-cfg             Fiay etsay onay onfigurationay ilefay illway ebay oadedlay
      --no-env             Fiay etsay hetay environmentay ariablesvay illway otnay ebay onsideredcay
      --pretty             Fiay etsay hetay onsolecay outputay ofay hetay ogginglay allscay illway ebay ettifiedpray
  -v, --verbosity int      Hetay erbosityvay ofay hetay ogrampray, anay integeray etweenbay 0 anday 3 inclusiveay. (default 1)

Localized command output

Illway etchfay http://127.0.0.1 ithway imeouttay 400 illisecondsmay
Example (Minimal)

Example_minimal shows a minimal application using the greery library. Invocations mimicking actual command-line usage setting the parameter value in different ways are provided for reference.

package main

import (
	"fmt"
	"io/ioutil"
	"os"

	"github.com/woodensquares/greenery"
)

type exampleMinimalConfig struct {
	*greenery.BaseConfig
	Timeout *greenery.IntValue `greenery:"get|timeout|t,      minimal-app.timeout,   TIMEOUT"`
}

func exampleNewMinimalConfig() *exampleMinimalConfig {
	cfg := &exampleMinimalConfig{
		BaseConfig: greenery.NewBaseConfig("minimal", map[string]greenery.Handler{
			"get<": exampleMinimalGetter,
		}),
		Timeout: greenery.NewIntValue("Timeout", 0, 1000),
	}

	if err := cfg.Timeout.SetInt(400); err != nil {
		panic("Could not initialize the timeout to its default")
	}

	return cfg
}

var exampleMinimalDocs = map[string]*greenery.DocSet{
	"en": &greenery.DocSet{
		Short: "URL fetcher",
		Usage: map[string]*greenery.CmdHelp{
			"get": &greenery.CmdHelp{
				Use:   "[URI to fetch]",
				Short: "Retrieves the specified page",
			},
		},
		CmdLine: map[string]string{
			"Timeout": "the timeout to use for the fetch",
		},
	},
}

func exampleMinimalGetter(lcfg greenery.Config, args []string) error {
	cfg := lcfg.(*exampleMinimalConfig)

	if len(args) != 1 {
		return fmt.Errorf("Invalid number of command line arguments")
	}

	cfg.Debugf("fetching %s, timeout %d", args[0], cfg.Timeout.Value)
	fmt.Printf("Will fetch %s with timeout %d milliseconds\n\n", args[0], cfg.Timeout.Value)
	return nil
}

func exampleMinimalMain() {
	cfg := exampleNewMinimalConfig()
	defer cfg.Cleanup()

	if err := cfg.Execute(cfg, exampleMinimalDocs); err != nil {
		fmt.Fprintf(os.Stderr, "Error executing: %s\n", err)
		os.Exit(1)
	}
}

// Example_minimal shows a minimal application using the greery
// library. Invocations mimicking actual command-line usage setting the
// parameter value in different ways are provided for reference.
func main() {
	fmt.Println("----------------------------------------------------------")

	// Execute with no arguments, will use the default timeout
	os.Args = []string{"minimal", "get", "http://127.0.0.1"}
	fmt.Println("Executing with the default timeout")
	exampleMinimalMain()

	// Execute with a commandline timeout argument
	os.Args = []string{"minimal", "get", "-t", "300", "http://127.0.0.1"}
	fmt.Println("Executing with a commandline timeout")
	exampleMinimalMain()

	// Create a configuration file with a timeout value
	cfgFile, err := ioutil.TempFile("", "minimal")
	if err != nil {
		fmt.Println("Cannot create a temporary file")
		os.Exit(1)
	}
	defer func() {
		_ = os.Remove(cfgFile.Name())
	}()

	if _, err = cfgFile.Write([]byte(`# Sample config file
[minimal-app]
timeout = 550
`)); err != nil {
		fmt.Println("Cannot write the config file")
		os.Exit(1)
	}
	if err := cfgFile.Close(); err != nil {
		fmt.Println("Cannot close the config file")
		os.Exit(1)
	}

	// Execute with the created configuration file
	os.Args = []string{"minimal", "get", "-c", cfgFile.Name(), "http://127.0.0.1"}
	fmt.Println("Executing with a configuration file")
	exampleMinimalMain()

	// Execute with an environment variable argument, together with the
	// configuration file, note the environment variable will have precedence
	if err := os.Setenv("MINIMAL_TIMEOUT", "200"); err != nil {
		fmt.Println("Cannot set an environment variable")
		os.Exit(1)
	}

	os.Args = []string{"minimal", "get", "-c", cfgFile.Name(), "http://127.0.0.1"}
	fmt.Println("Executing with a configuration and environment variable timeout")
	exampleMinimalMain()

	// Executing with commandline, config and environment timeout argument,
	// the commandline will take precedence
	os.Args = []string{"minimal", "get", "-c", cfgFile.Name(), "-t", "300", "http://127.0.0.1"}
	fmt.Println("Executing with a commandline, configuration and environment timeout")
	exampleMinimalMain()

}
Output:

----------------------------------------------------------
Executing with the default timeout
Will fetch http://127.0.0.1 with timeout 400 milliseconds

Executing with a commandline timeout
Will fetch http://127.0.0.1 with timeout 300 milliseconds

Executing with a configuration file
Will fetch http://127.0.0.1 with timeout 550 milliseconds

Executing with a configuration and environment variable timeout
Will fetch http://127.0.0.1 with timeout 200 milliseconds

Executing with a commandline, configuration and environment timeout
Will fetch http://127.0.0.1 with timeout 300 milliseconds

Index

Examples

Constants

View Source
const (
	// DocBaseDelimiter is the delimiter to be used for the base section, this
	// will map to the toplevel of the DocSet struct.
	DocBaseDelimiter = doc.BaseDelimiter

	// DocUse contains the text to display in the use line for the application
	// itself, this is typically used if the root command has a handler which
	// takes arguments. The text to be used should follow in the next string in
	// the list. The string following will be assigned to DocSet.Use.
	DocUse = doc.Use

	// DocLong is the long description for the application as a whole. The text to
	// be used should follow in the next string in the list. The string following
	// will be assigned to DocSet.Long.
	DocLong = doc.Long

	// DocShort is the short description for the application as a whole. The text
	// to be used should follow in the next string in the list.. The string
	// following will be assigned to DocSet.Short.
	DocShort = doc.Short

	// DocExample is an example to be used for the application as a whole. The
	// text to be used should follow in the next string in the list. . The string
	// following will be assigned to DocSet.Example.
	DocExample = doc.Example

	// DocHelpFlag is the text displayed next to the help command when printing
	// the help for the application. The text to be used should follow in the next
	// string. The string following will be assigned to DocSet.HelpFlag.
	DocHelpFlag = doc.HelpFlag

	// DocCmdFlags is the text to use for "[flags]" as printed in command help
	// messages. The text to be used should follow in the next string in the
	// list. The string following will be assigned to DocSet.CmdFlags.
	DocCmdFlags = doc.CmdFlags

	// DocConfigEnvMsg1 is part of the localized text to be used for the config
	// env command. The text to be used should follow in the next string in the
	// list. The string following will be assigned to DocSet.ConfigEnvMsg1.
	DocConfigEnvMsg1 = doc.ConfigEnvMsg1

	// DocConfigEnvMsg2 is part of the localized text to be used for the config
	// env command. The text to be used should follow in the next string in the
	// list. The string following will be assigned to DocSet.ConfigEnvMsg2.
	DocConfigEnvMsg2 = doc.ConfigEnvMsg2

	// DocConfigEnvMsg3 is part of the localized text to be used for the config
	// env command. The text to be used should follow in the next string in the
	// list. The string following will be assigned to DocSet.ConfigEnvMsg3.
	DocConfigEnvMsg3 = doc.ConfigEnvMsg3

	// DocHelpDelimiter is the delimiter of the help section. This will map to the
	// Help field in the DocSet struct, which is a HelpStrings struct. These
	// strings will be used to localize the overall common headers/footers/... in
	// various help messages.
	DocHelpDelimiter = doc.HelpDelimiter

	// DocUsage controls the "Usage:" sentence. The text to be used should follow in
	// the next string in the list. The string following will be assigned to
	// HelpStrings.Usage.
	DocUsage = doc.Usage

	// DocAliases controls the "Aliases:" sentence. The text to be used should
	// follow in the next string in the list. The string following will be
	// assigned to HelpStrings.Aliases.
	DocAliases = doc.Aliases

	// DocExamples controls the "Examples:" sentence. The text to be used should
	// follow in the next string in the list. The string following will be assigned to
	// HelpStrings.Examples.
	DocExamples = doc.Examples

	// DocAvailableCommands controls the "Available Commands:" sentence. The text
	// to be used should follow in the next string in the list. The string
	// following will be assigned to HelpStrings.AvailableCommands.
	DocAvailableCommands = doc.AvailableCommands

	// DocFlags controls the "Flags:" sentence. The text to be used should follow
	// in the next string in the list. The string following will be assigned to
	// HelpStrings.Flags.
	DocFlags = doc.Flags

	// DocGlobalFlags controls the "Global Flags:" sentence. The text to be used
	// should follow in the next string in the list. The string following will be
	// assigned to HelpStrings.GlobalFlags.
	DocGlobalFlags = doc.GlobalFlags

	// DocAdditionalHelpTopics controls the "Additional help topics:"
	// sentence. The text to be used should follow in the next string in the
	// list. The string following will be assigned to
	// HelpStrings.AdditionalHelpTopics.
	DocAdditionalHelpTopics = doc.AdditionalHelpTopics

	// DocProvidesMoreInformationAboutACommand controls the "provides more
	// information about a command." sentence. The text to be used should follow
	// in the next string in the list. The string following will be assigned to
	// HelpStrings.ProvidesMoreInformationAboutACommand.
	DocProvidesMoreInformationAboutACommand = doc.ProvidesMoreInformationAboutACommand

	// DocUsageDelimiter is the delimiter for the usage section, this will map to
	// the Usage field in the DocSet struct, which is a CmdHelp struct. This is
	// used to contain help for individual commands, existing and
	// user-created. Each command is controlled by a sequence of five strings,
	// they are in order:
	//
	// identifier of the command (which is the key in the handlers map, this can
	//                            also be one of the predefined identifiers below)
	// usage string (in case the command has arguments), will map to CmdHelp.Use
	// short help for the command, will map to CmdHelp.Short
	// long help for the command, will map to CmdHelp.Long
	// example for the command, will map to CmdHelp.Example
	//
	// Internal commands have predefined constants, so they can be used instead of
	// writing things like "config>init" which might be confusing for the
	// localizers.
	DocUsageDelimiter = doc.UsageDelimiter

	// DocConfigCmd is the identifier for the "config" command.
	DocConfigCmd = doc.ConfigCmd

	// DocConfigInitCmd is the identifier for the "config init" command.
	DocConfigInitCmd = doc.ConfigInitCmd

	// DocConfigEnvCmd is the identifier for the "config env" command.
	DocConfigEnvCmd = doc.ConfigEnvCmd

	// DocConfigDisplayCmd is the identifier for the "config display" command.
	DocConfigDisplayCmd = doc.ConfigDisplayCmd

	// DocHelpCmd is the identifier for the "help" command.
	DocHelpCmd = doc.HelpCmd

	// DocVersionCmd is the identifier for the "version" command.
	DocVersionCmd = doc.VersionCmd

	// DocCmdlineDelimiter is the delimiter for the command line flags section,
	// this will map to the CmdLine field in the DocSet struct, which is a map of
	// strings. In this section the format is simply
	//
	// name of the variable corresponding to the flag
	// help information for this flag to be displayed
	//
	// as in the usage section above, predefined identifiers are included for the
	// base flags defined by the library.
	DocCmdlineDelimiter = doc.CmdlineDelimiter

	// DocLogLevel is the help information for the LogLevel flag.
	DocLogLevel = doc.LogLevel

	// DocConfFile is the help information for the ConfFile flag.
	DocConfFile = doc.ConfFile

	// DocLogFile is the help information for the LogFile flag.
	DocLogFile = doc.LogFile

	// DocPretty is the help information for the Pretty flag.
	DocPretty = doc.Pretty

	// DocNoEnv is the help information for the NoEnv flag.
	DocNoEnv = doc.NoEnv

	// DocNoCfg is the help information for the NoCfg flag.
	DocNoCfg = doc.NoCfg

	// DocVerbosity is the help information for the Verbosity flag.
	DocVerbosity = doc.Verbosity

	// DocDoTrace is the help information for the DoTrace flag.
	DocDoTrace = doc.DoTrace

	// DocCfgLocation is the help information for the CfgLocation flag.
	DocCfgLocation = doc.CfgLocation

	// DocCfgForce is the help information for the CfgForce flag.
	DocCfgForce = doc.CfgForce

	// DocConfigDelimiter is the delimiter for the config file section, this will
	// map to the ConfigFile field in the DocSet struct, which is a map of
	// strings. This section has the exact same structure as the Cmdline section
	// and should contain only entries for which the text between the command line
	// and config file help is different. In addition it also typically contains
	// the help displayed for custom configuration sections, the predefined base
	// section name follows.
	DocConfigDelimiter = doc.ConfigDelimiter

	// DocConfigHeader is the name of the base configuration file section, this is
	// going to be treated as a golang template, {{ date }} is available and will
	// be substituted with the date/time the configuration file has been generated
	// at.
	DocConfigHeader = doc.ConfigHeader

	// DocCustomDelimiter is the delimiter for the custom file section, this
	// will map to the Custom field in the DocSet struct, which is a map of
	// strings. This section is not used by the standard library but is
	// available for custom string localizations
	DocCustomDelimiter = doc.CustomDelimiter
)

These constants are to be used when creating string-list user documentation, they contain identifiers used for the section delimiters, as well as for all the individual options.

Variables

This section is empty.

Functions

func ConvertOrPanic

func ConvertOrPanic(unprocessedDocs map[string][]string) map[string]*DocSet

ConvertOrPanic is used to convert a set of documents from the []string format to the final map[string]*DocSet one, this function will panic in case there are any errors in the conversion.

Types

type BaseConfig

type BaseConfig struct {

	// LogLevel maps to the loglevel options, it contains the requested
	// logging level.
	LogLevel *EnumValue `greenery:"|log-level|l, .log-level, LOGLEVEL"`

	// ConfFile maps to the config file options, it contains the name of the
	// requested configuration file.
	ConfFile string `greenery:"|config|c, , CONFIGFILE"`

	// LogFile maps to the log file options, it contains the name of the
	// requested log file.
	LogFile string `greenery:"|log-file|, .log-file, LOGFILE"`

	// Pretty maps to the pretty options, it contains whether pretty logging
	// has been requested.
	Pretty bool `greenery:"|pretty|, .pretty, PRETTY"`

	// NoEnv maps to the no-environment options, it contains whether the user
	// requested to not consult the environment when setting configuration
	// values.
	NoEnv bool `greenery:"|no-env|, .no-env,"`

	// NoCfg maps to the no-cfg options, it contains whether the
	// user requested to not consult the configuration file when setting
	// configuration values.
	NoCfg bool `greenery:"|no-cfg|, ,NOCFG"`

	// Verbosity maps to the verbosity options, it contains the requested
	// level of verbosity.
	Verbosity *IntValue `greenery:"|verbosity|v, .verbosity, VERBOSITY"`

	// DoTrace maps to the tracing options, it contains whether the user
	// requested tracing output.
	DoTrace bool `greenery:"|trace|hidden, , TRACE"`

	// CfgForce maps to the --force parameter to the config init command, this
	// controls whether an already existing configuration file will be
	// overwritten by the command.
	CfgForce bool `greenery:"config>init|force|,,"`

	// CfgLocation maps to the --location parameter to the config init
	// command, this controls where the configuration file is created.
	CfgLocation *EnumValue `greenery:"config>init|location|,,"`

	// Values users is expected to set as part of their configuration init
	// function. Users might need to access these directly in their code
	// afterwards (for example to implement version compatibility, or to check
	// the default language for error message purposes) so these are public.
	//
	// For versions VersionFull has precedence over major/minor/patchlevel so
	// will be printed instead if set.
	// ------------------------------------------------------------------
	VersionFull       string `greenery:"||none,,"`
	VersionMajor      string `greenery:"||none,,"`
	VersionMinor      string `greenery:"||none,,"`
	VersionPatchlevel string `greenery:"||none,,"`
	// contains filtered or unexported fields
}

BaseConfig is the default base configuration, that needs to be embedded in all user configuration structs. It contains all the needed internal variables as well as exported alues for the built-in configuration values, like the current log level, log file, configuration file name and so on.

func NewBaseConfig

func NewBaseConfig(appname string, fmap map[string]Handler) *BaseConfig

NewBaseConfig returns an initialized BaseConfig struct, it requires the application name and a map of handlers for the application commands.

func (*BaseConfig) Cleanup

func (cfg *BaseConfig) Cleanup()

Cleanup should be called at the end of the program, it will close and remove any temporary files, and sync the log.

func (*BaseConfig) Debug

func (cfg *BaseConfig) Debug(s string)

Debug is used to debug log to the current logger with verbosity 2 (typical)

func (*BaseConfig) Debugf

func (cfg *BaseConfig) Debugf(format string, vals ...interface{})

Debugf is used to sprintf debug log to the current logger with verbosity 2 (typical)

func (*BaseConfig) Debugq

func (cfg *BaseConfig) Debugq(s string)

Debugq is used to debug log to the current logger with verbosity 1

func (*BaseConfig) Debugqf

func (cfg *BaseConfig) Debugqf(format string, vals ...interface{})

Debugqf is used to sprintf debug log to the current logger with verbosity 1

func (*BaseConfig) Debugqs

func (cfg *BaseConfig) Debugqs(s string, vals ...LogField)

Debugqs is used to structured debug log to the current logger with verbosity 1

func (*BaseConfig) Debugs

func (cfg *BaseConfig) Debugs(s string, vals ...LogField)

Debugs is used to structured debug log to the current logger with verbosity 2 (typical)

func (*BaseConfig) Debugv

func (cfg *BaseConfig) Debugv(s string)

Debugv is used to debug log to the current logger with verbosity 3

func (*BaseConfig) Debugvf

func (cfg *BaseConfig) Debugvf(format string, vals ...interface{})

Debugvf is used to sprintf debug log to the current logger with verbosity 3

func (*BaseConfig) Debugvs

func (cfg *BaseConfig) Debugvs(s string, vals ...LogField)

Debugvs is used to structured debug log to the current logger with verbosity 3

func (*BaseConfig) Dump

func (cfg *BaseConfig) Dump(icfg Config) (string, error)

Dump will print on stdout the current configuration values

func (*BaseConfig) Error

func (cfg *BaseConfig) Error(s string)

Error is used to error log to the current logger, errorings are always logged unless v is 0, so no need to have custom selectable verbosity

func (*BaseConfig) Errorf

func (cfg *BaseConfig) Errorf(format string, vals ...interface{})

Errorf is used to sprintf error log to the current logger, errorings are always logged unless v is 0, so no need to have custom selectable verbosity

func (*BaseConfig) Errors

func (cfg *BaseConfig) Errors(s string, vals ...LogField)

Errors is used to structured error log to the current logger, errorings are always logged unless v is 0, so no need to have custom selectable verbosity

func (*BaseConfig) Execute

func (cfg *BaseConfig) Execute(icfg Config, userDocList map[string]*DocSet) error

Execute is the main entry point for the library, it requires an initialized configuration struct, as well as the documentation strings to be used for the program.

func (*BaseConfig) GetConfigFile

func (cfg *BaseConfig) GetConfigFile() string

GetConfigFile returns the filename of the configuration file used.

func (*BaseConfig) GetCurrentCommand

func (cfg *BaseConfig) GetCurrentCommand() string

GetCurrentCommand returns the name of the currently executing command, note that the root/base command is returned as ""

func (*BaseConfig) GetDefaultLanguage

func (cfg *BaseConfig) GetDefaultLanguage() string

GetDefaultLanguage return the default language set in the configuration

func (*BaseConfig) GetDocs

func (cfg *BaseConfig) GetDocs() (string, *DocSet)

GetDocs returns the current docset in use

func (*BaseConfig) GetFs

func (cfg *BaseConfig) GetFs() afero.Fs

GetFs returns the filesystem currently in use

func (*BaseConfig) GetLogger

func (cfg *BaseConfig) GetLogger() Logger

GetLogger returns the current logger

func (*BaseConfig) Info

func (cfg *BaseConfig) Info(s string)

Info is used to info log to the current logger with verbosity 2 (typical)

func (*BaseConfig) Infof

func (cfg *BaseConfig) Infof(format string, vals ...interface{})

Infof is used to sprintf info log to the current logger with verbosity 2 (typical)

func (*BaseConfig) Infoq

func (cfg *BaseConfig) Infoq(s string)

Infoq is used to info log to the current logger with verbosity 1

func (*BaseConfig) Infoqf

func (cfg *BaseConfig) Infoqf(format string, vals ...interface{})

Infoqf is used to sprintf info log to the current logger with verbosity 1

func (*BaseConfig) Infoqs

func (cfg *BaseConfig) Infoqs(s string, vals ...LogField)

Infoqs is used to structured info log to the current logger with verbosity 1

func (*BaseConfig) Infos

func (cfg *BaseConfig) Infos(s string, vals ...LogField)

Infos is used to structured info log to the current logger with verbosity 2 (typical)

func (*BaseConfig) Infov

func (cfg *BaseConfig) Infov(s string)

Infov is used to info log to the current logger with verbosity 3

func (*BaseConfig) Infovf

func (cfg *BaseConfig) Infovf(format string, vals ...interface{})

Infovf is used to sprintf info log to the current logger with verbosity 3

func (*BaseConfig) Infovs

func (cfg *BaseConfig) Infovs(s string, vals ...LogField)

Infovs is used to structured info log to the current logger with verbosity 3

func (*BaseConfig) LogDuration

func (cfg *BaseConfig) LogDuration(s string, value time.Duration) LogField

LogDuration is returns a greenery LogField for a time.Duration logging value

func (*BaseConfig) LogGeneric

func (cfg *BaseConfig) LogGeneric(s string, value interface{}) LogField

LogGeneric is returns a greenery LogField for a generic logging value

func (*BaseConfig) LogInteger

func (cfg *BaseConfig) LogInteger(s string, value int) LogField

LogInteger returns a greenery LogField for an int logging value

func (*BaseConfig) LogString

func (cfg *BaseConfig) LogString(s string, value string) LogField

LogString returns a greenery LogField for a string logging value

func (*BaseConfig) LogTime

func (cfg *BaseConfig) LogTime(s string, value time.Time) LogField

LogTime returns a greenery LogField for a time.Time logging value

func (*BaseConfig) RegisterExtraParse

func (cfg *BaseConfig) RegisterExtraParse(f func(Config, map[string]interface{}) ([]string, error), a []string)

RegisterExtraParse allows additional parsing for the configuration file, see the "custom" example for a usage example.

func (*BaseConfig) SetFs

func (cfg *BaseConfig) SetFs(fs afero.Fs)

SetFs allows the filesystem used by the library to be set

func (*BaseConfig) SetHandler

func (cfg *BaseConfig) SetHandler(handler OverrideHandler, h Handler) error

SetHandler allows overriding of the handlers for the built-in base, config and version commands. In addition it allows to set a handler that will be invoked just before passing control to the requested command handler.

func (*BaseConfig) SetLoggers

func (cfg *BaseConfig) SetLoggers(structured, pretty MakeLogger, trace MakeTraceLogger) error

SetLoggers sets the specified loggers creators, if any of the values is set to nil, the specified logging type will not be available. For example if nil is passed in the trace parameter, trace logging would not be available even if requested by the user with --trace.

func (*BaseConfig) SetOptions

func (cfg *BaseConfig) SetOptions(opts BaseConfigOptions) error

SetOptions allows to set configuration options before the Execute call is made.

func (*BaseConfig) StartTracing

func (cfg *BaseConfig) StartTracing()

StartTracing is used to start trace logging.

func (*BaseConfig) StopTracing

func (cfg *BaseConfig) StopTracing()

StopTracing is used to stop trace logging

func (*BaseConfig) TestHelper

func (cfg *BaseConfig) TestHelper(task string, value interface{}) interface{}

TestHelper is used by tests to change some internals if needed

func (*BaseConfig) Trace

func (cfg *BaseConfig) Trace(s string)

Trace is used to debug log to the trace logger, this is a separate stderr non-redirectable / non-configurable logger

func (*BaseConfig) TraceSkipf

func (cfg *BaseConfig) TraceSkipf(skip int, format string, vals ...interface{})

TraceSkipf will tracef log but skipping additional frames in case .Trace is being passed around as a function.

func (*BaseConfig) Tracef

func (cfg *BaseConfig) Tracef(format string, vals ...interface{})

Tracef is used to sprintf debug log to the trace logger, this is a separate stderr non-redirectable / non-configurable logger

func (*BaseConfig) Unmarshal

func (cfg *BaseConfig) Unmarshal(s string, v interface{}) error

Unmarshal allows unmarshaling of a single configuration key, if that key type is supported by the underlying TOML library. See the "custom" example for a usage example.

func (*BaseConfig) Warn

func (cfg *BaseConfig) Warn(s string)

Warn is used to warn log to the current logger, warnings are always logged unless v is 0, so no need to have custom selectable verbosity

func (*BaseConfig) Warnf

func (cfg *BaseConfig) Warnf(format string, vals ...interface{})

Warnf is used to sprintf warn log to the current logger, warnings are always logged unless v is 0, so no need to have custom selectable verbosity

func (*BaseConfig) Warns

func (cfg *BaseConfig) Warns(s string, vals ...LogField)

Warns is used to structured warn log to the current logger, warnings are always logged unless v is 0, so no need to have custom selectable verbosity

type BaseConfigOptions

type BaseConfigOptions struct {
	DefaultLanguage   string
	VersionFull       string
	VersionMajor      string
	VersionMinor      string
	VersionPatchlevel string
}

BaseConfigOptions can be used to set the same-named variables in a configuration to the specified values. DefaultLanguage will be ignored if empty, version strings will be assigned as-is

type CmdHelp

type CmdHelp struct {
	// Use contains the use information for the command, if it takes arguments
	Use string
	// Short contains the short documentation for the command, typically
	// displayed in the parent command's help
	Short string
	// Long contains the long documentation for the command
	Long string
	// Example contains any examples for this command
	Example string
}

CmdHelp contains the Cobra use/short/long/example documentation strings in a particular language.

type Config

type Config interface {
	Cleanup()
	Dump(Config) (string, error)
	Execute(Config, map[string]*DocSet) error
	GetConfigFile() string
	GetCurrentCommand() string
	GetDefaultLanguage() string
	GetDocs() (string, *DocSet)
	GetFs() afero.Fs
	RegisterExtraParse(func(Config, map[string]interface{}) ([]string, error), []string)
	SetFs(afero.Fs)
	SetHandler(OverrideHandler, Handler) error
	SetOptions(BaseConfigOptions) error
	GetLogger() Logger
	SetLoggers(MakeLogger, MakeLogger, MakeTraceLogger) error
	Unmarshal(string, interface{}) error

	Debugq(string)
	Debugqf(string, ...interface{})
	Debugqs(string, ...LogField)
	Debug(string)
	Debugf(string, ...interface{})
	Debugs(string, ...LogField)
	Debugv(string)
	Debugvf(string, ...interface{})
	Debugvs(string, ...LogField)

	Error(string)
	Errorf(string, ...interface{})
	Errors(string, ...LogField)

	Infoq(string)
	Infoqf(string, ...interface{})
	Infoqs(string, ...LogField)
	Info(string)
	Infof(string, ...interface{})
	Infos(string, ...LogField)
	Infov(string)
	Infovf(string, ...interface{})
	Infovs(string, ...LogField)

	Warn(string)
	Warnf(string, ...interface{})
	Warns(string, ...LogField)

	StartTracing()
	StopTracing()
	Trace(string)
	Tracef(string, ...interface{})

	LogInteger(string, int) LogField
	LogGeneric(string, interface{}) LogField
	LogString(string, string) LogField
	LogTime(string, time.Time) LogField
	LogDuration(string, time.Duration) LogField

	// Used by tests only to tweak internals as needed
	TestHelper(string, interface{}) interface{}
	// contains filtered or unexported methods
}

Config is the interface fulfilled by a configuration struct that can be used by the greenery library. It is a sealed interface and can be fulfilled only by BaseConfig structs, or structs embedding it. See the documentation in BaseStruct for more information on the individual methods.

type CustomStringHandler

type CustomStringHandler func(string, string, interface{}) (string, error)

CustomStringHandler defines a function used to validate the custom string for correctness. This function will be passed, in order, the name of this flag, the value the user would like to set, and the arbitrary flag data. The function should return the value that will be set in the flag, which could be different from what the user sends, and an error if the value is not acceptable.

type CustomStringValue

type CustomStringValue struct {
	Value string
	// contains filtered or unexported fields
}

CustomStringValue is a flag struct that has a customizeable validator / converter.

func NewCustomStringValue

func NewCustomStringValue(name string, validate CustomStringHandler, data interface{}) *CustomStringValue

NewCustomStringValue returns a new CustomStringValue flag, the default value is set to whatever the validation function will return when passed the empty string. Name is a name for this flag, typically the configuration variable name, and is used for error messages. Data is arbitrary data that will be stored in the flag and passed to the custom validation function on validation calls.

func NewDefaultCustomStringValue

func NewDefaultCustomStringValue(name string, set string, validate CustomStringHandler, data interface{}) *CustomStringValue

NewDefaultCustomStringValue returns a new CustomStringValue flag with the default value being what the validation function will return when passed the string specified in set. Name is a name for this flag, typically the configuration variable name, and is used for error messages. Data is arbitrary data that will be stored in the flag and passed to the custom validation function on validation calls. If the set value does not fulfill the validate function, this function will panic.

func (*CustomStringValue) GetTyped

func (c *CustomStringValue) GetTyped() string

GetTyped is typically used for tests and returns the flag string value

func (*CustomStringValue) MarshalText

func (c *CustomStringValue) MarshalText() (text []byte, err error)

MarshalText is used for TOML configuration file marshaling, it is used when generating the config files via config generate.

func (*CustomStringValue) Set

func (c *CustomStringValue) Set(s string) (err error)

Set will set the string value, while validating for correctness by calling the custom validation function.

func (*CustomStringValue) String

func (c *CustomStringValue) String() string

String will return the string flag value

func (*CustomStringValue) Type

func (c *CustomStringValue) Type() string

Type will return a string describing the type of the flag, it is required to fulfill pflag.Value and will be printed in the help messages

func (*CustomStringValue) UnmarshalText

func (c *CustomStringValue) UnmarshalText(text []byte) error

UnmarshalText is used for TOML configuration file unmarshaling, and will set the value in the flag with validation.

type DocSet

type DocSet struct {
	// Use contains a string appended to the application name, when displaying
	// the help for the root command. It can be useful, for example, if a
	// positional parameter is used in the application: for example if the
	// application is named "tester" and it is going to support a URI of the
	// site to test as a parameter, this string could be set to "[website
	// address]".
	Use string

	// Short contains the short description for the program (displayed for the
	// root command)
	Short string

	// Long contains the short description for the program (displayed for the
	// root command)
	Long string

	// Example contains usage examples for the command
	Example string

	// HelpFlag contains the help string displayed in the root command help
	// for the --help help flag.
	HelpFlag string

	// CmdFlags contains the string corresponding to [flags] which is printed
	// after the command in the usage line if required.
	CmdFlags string

	// ConfigEnvMsg1 contains the first part of the localizable message for
	// the config env command
	ConfigEnvMsg1 string

	// ConfigEnvMsg2 contains the second part of the localizable message for
	// the config env command
	ConfigEnvMsg2 string

	// ConfigEnvMsg3 contains the third and final part of the localizable
	// message for the config env command
	ConfigEnvMsg3 string

	// Help points to a struct that contains the strings used in the help
	// template itself (things like "Aliases:" etc.)
	Help *HelpStrings

	// CmdLine contains the doc strings displayed in each command-line flag,
	// for the specified configuration file variable. This map is keyed by the
	// file variable name.
	CmdLine map[string]string

	// ConfigFile contains the doc strings that will be put in the
	// autogenerated configuration file before each configuration file
	// variable. This map is also keyed by the configuration file variable
	// name. These will be automatically commented when put in the TOML
	// configuration file unless they refer to a variable with a custom
	// deserialization, in which case they will be printed as-is in order to
	// allow providing of arbitrary default values.
	ConfigFile map[string]string

	// Usage contains the command-specific usage information, the key is the
	// command name as defined in the command map that is passed to Execute.
	Usage map[string]*CmdHelp

	// Custom contains user-supplied localized strings
	Custom map[string]string
}

DocSet contains the config documentation for a specific language

func ConvertDocs

func ConvertDocs(udoc []string) (*DocSet, error)

ConvertDocs is used to convert a []string documentation to the internal *DocSet representation, it will return an error if there are any parsing issues. Note the code can't be super precise in the validation given a single list of strings with arbitrary names, it tries to catch at least obvious things like multiple sections or wrong number of lines in a section etc.

type EnumValue

type EnumValue struct {
	*CustomStringValue
}

EnumValue is a CustomStringValue flag struct containing, and validating for, a specified set of acceptable string values set on creation.

func NewDefaultEnumValue

func NewDefaultEnumValue(name, set string, valid ...string) *EnumValue

NewDefaultEnumValue returns a new EnumValue flag, its value is set to the passed set value. Name is a name for this flag, typically the configuration variable name, and is used for error messages. valid is a set of valid values for this flag, at least one must be present. If the set value is not a valid enum value this function will panic.

func NewEnumValue

func NewEnumValue(name string, valid ...string) *EnumValue

NewEnumValue returns a new EnumValue flag, its value is set to the first value in the valid enums list. Name is a name for this flag, typically the configuration variable name, and is used for error messages. valid is a set of valid values for this flag, at least one must be present.

type Handler

type Handler func(Config, []string) error

Handler is the user-provided function that will be called by the library when the specified command is selected. It will be passed the current configuration, as well as a list of command-line arguments.

type HelpStrings

type HelpStrings struct {
	// Usage contains the localized "Usage:" sentence
	Usage string
	// Aliases contains the localized "Aliases:" sentence
	Aliases string
	// Examples contains the localized "Examples:" sentence
	Examples string
	// AvailableCommands contains the localized "Available Commands:" sentence
	AvailableCommands string
	// Flags contains the localized "Flags:" sentence
	Flags string
	// GlobalFlags contains the localized "Global Flags:" sentence
	GlobalFlags string
	// AdditionalHelpTopics contains the localized "Additional help topics:"
	// sentence
	AdditionalHelpTopics string
	// ProvidesMoreInformationAboutACommand contains the localized "provides
	// more information about a command." sentence
	ProvidesMoreInformationAboutACommand string
}

HelpStrings contains the strings used for the various portions of the template.

type IPValue

type IPValue struct {
	*CustomStringValue
}

IPValue is a CustomStringValue flag struct containing, and validating for, an IP address (v4 or v6).

func NewDefaultIPValue

func NewDefaultIPValue(name, set string) *IPValue

NewDefaultIPValue returns a new IPValue flag set to the passed set value. Name is a name for this flag, typically the configuration variable name, and is used for error messages. If the set value is not a valid IP, this function will panic.

func NewIPValue

func NewIPValue(name string) *IPValue

NewIPValue returns a new IPValue flag, its value is set to "0.0.0.0". Name is a name for this flag, typically the configuration variable name, and is used for error messages.

type IntValue

type IntValue struct {
	Value int
	// contains filtered or unexported fields
}

IntValue is a flag struct containing an integer with customizeable minimum and maximum values.

func NewDefaultIntValue

func NewDefaultIntValue(name string, set, min, max int) *IntValue

NewDefaultIntValue returns a new IntValue flag set to the specified "set" value. Name is a name for this flag, typically the configuration variable name, and is used for error messages. Min and max are the values to be used as min/max values to validate against.

func NewIntValue

func NewIntValue(name string, min, max int) *IntValue

NewIntValue returns a new IntValue flag, its value is set to min. Name is a name for this flag, typically the configuration variable name, and is used for error messages. Min and max are the values to be used as min/max values to validate against.

func (*IntValue) GetTyped

func (i *IntValue) GetTyped() int

GetTyped is typically used for tests and returns the flag int value

func (*IntValue) MarshalText

func (i *IntValue) MarshalText() (text []byte, err error)

MarshalText is used for TOML configuration file marshaling, it is used when generating the config files via config generate.

func (*IntValue) Set

func (i *IntValue) Set(s string) (err error)

Set will set a value parsing from a string, while validating for correctness

func (*IntValue) SetInt

func (i *IntValue) SetInt(d int) (err error)

SetInt will set an integer and validate it for correctness

func (*IntValue) String

func (i *IntValue) String() string

String will return a string representation of the flag value

func (*IntValue) Type

func (i *IntValue) Type() string

Type will return a string describing the type of the flag, it is required to fulfill pflag.Value and will be printed in the help messages

func (*IntValue) UnmarshalText

func (i *IntValue) UnmarshalText(text []byte) error

UnmarshalText is used for TOML configuration file unmarshaling, and will set the value in the flag with validation.

type LogField

type LogField struct {
	Key      string
	Type     uint8
	Integer  int
	Time     time.Time
	Duration time.Duration
	String   string
	Generic  interface{}
}

LogField is used by the structured logging functions to pass around information about the specific field. Each logger can decide how to use these fields as they see fit.

type Logger

type Logger interface {
	Custom(string, int, interface{})
	DebugStructured(string, ...LogField)
	InfoStructured(string, ...LogField)
	WarnStructured(string, ...LogField)
	ErrorStructured(string, ...LogField)

	LogString(string, string) LogField
	LogInteger(string, int) LogField
	LogTime(string, time.Time) LogField
	LogDuration(string, time.Duration) LogField
	LogGeneric(string, interface{}) LogField

	Sync() error

	// For testing purposes primarily
	DebugSkip(int, string)
}

Logger is an interface that describes a logger provided by logger.go, currently this is just a basic printf logger. A zap logger is available in zaplogger/ if a structured or more high performant logger is required.

func BasePrettyLogger

func BasePrettyLogger(cfg Config, l string, w io.Writer) Logger

BasePrettyLogger represents a base logger used for pretty logging

func BaseStructuredLogger

func BaseStructuredLogger(cfg Config, l string, w io.Writer) Logger

BaseStructuredLogger represents a base logger used for structured logging, although for base logs this is not supported and it will behave the same way as the pretty logger for the time being

func BaseTraceLogger

func BaseTraceLogger(cfg Config) Logger

BaseTraceLogger represents a base logger used for Tracing

type MakeLogger

type MakeLogger func(Config, string, io.Writer) Logger

MakeLogger describes a function that would return a normal logger

type MakeTraceLogger

type MakeTraceLogger func(Config) Logger

MakeTraceLogger describes a function that would return a trace logger

type OverrideHandler

type OverrideHandler int

OverrideHandler allows to select which internal command to override in calls to SetHandler

const (
	// OverrideRootHandler selects the root command
	OverrideRootHandler OverrideHandler = iota

	// OverrideConfigHandler selects the config command
	OverrideConfigHandler

	// OverrideVersionHandler selects the version command
	OverrideVersionHandler

	// OverridePreExecHandler allows a handler that will be executed before the
	// actual command handler.
	OverridePreExecHandler
)

type PortValue

type PortValue uint16

PortValue is a flag representing a port value, from 0 to 65535. An IntValue flag with these limits could have also been used, either directly or as an embedded struct.

func NewPortValue

func NewPortValue() PortValue

NewPortValue returns a new PortValue flag, its value is set to 0.

func (*PortValue) GetTyped

func (p *PortValue) GetTyped() uint16

GetTyped is typically used for tests and returns the flag uint16 value

func (*PortValue) MarshalText

func (p *PortValue) MarshalText() (text []byte, err error)

MarshalText is used for TOML configuration file marshaling, it is used when generating the config files via config generate.

func (*PortValue) Set

func (p *PortValue) Set(e string) (err error)

Set will set a value parsing from a string, while validating for correctness

func (*PortValue) SetInt

func (p *PortValue) SetInt(i int) (err error)

SetInt will set an integer and validate it for correctness

func (*PortValue) String

func (p *PortValue) String() string

String will return a string representation of the flag value

func (*PortValue) Type

func (p *PortValue) Type() string

Type will return a string describing the type of the flag, it is required to fulfill pflag.Value and will be printed in the help messages

func (*PortValue) UnmarshalText

func (p *PortValue) UnmarshalText(text []byte) error

UnmarshalText is used for TOML configuration file unmarshaling, and will set the value in the flag with validation.

Directories

Path Synopsis
internal
doc
Package greenery/testhelper is a helper library used to test greenery-based applications.
Package greenery/testhelper is a helper library used to test greenery-based applications.
Package greenery/zapbackend is a sample zap logging back-end for greenery based applications.
Package greenery/zapbackend is a sample zap logging back-end for greenery based applications.

Jump to

Keyboard shortcuts

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