Categorygithub.com/pschou/go-params
modulepackage
0.0.0-20240104134853-225c817afb69
Repository: https://github.com/pschou/go-params.git
Documentation: pkg.go.dev

# README

go-params

POSIX compliant argument parser for Go!

Usage Documentation: https://pkg.go.dev/github.com/pschou/go-params

Introduction

What would the module look like if GoLang provided a full-featured flag/parameter parsing package? What if it offers flexibility, simplicity while also maintaining the familiar look-and-feel other open-source packages provide? As programmers, we must provide that comfort level to our users, match the look-and-feel used by other commonly used Linux packages. This a community effort as by matching other common usage packages, we ultimately lower our users' learning curve and blend in with the rest of the technologies available. Welcome to the solution for GoLang that does just this, go-param.

As there are many examples of programs that handle parameters differently, let us choose two commonly used packages of which to model and build a generic module to mimic. The two selected are ldapsearch and curl. The first has been around for over two decades, and the second is fairly new. Both are well used and understood by the Linux community as a whole. The goal here is to lower the bar of learning and make the flags operate as close as other linux tools operate to ease user's learning curve. The goal is finished! With this param package, GoLang can output the same help and parse the same parameter inputs.

Src: https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html

POSIX recommends these conventions for command line arguments. getopt (see Getopt) and argp_parse (see Argp) make it easy to implement them.

  • Arguments are options if they begin with a hyphen delimiter (‘-’).
  • Multiple options may follow a hyphen delimiter in a single token if the options do not take arguments. Thus, ‘-abc’ is equivalent to ‘-a -b -c’.
  • Option names are single alphanumeric characters (as for isalnum; see Classification of Characters, https://www.gnu.org/software/libc/manual/html_node/Classification-of-Characters.html).
  • Certain options require an argument. For example, the ‘-o’ command of the ld command requires an argument—an output file name.
  • An option and its argument may or may not appear as separate tokens. (In other words, the whitespace separating them is optional.) Thus, ‘-o foo’ and ‘-ofoo’ are equivalent.
  • Options typically precede other non-option arguments.

The implementations of getopt and argp_parse in the GNU C Library normally make it appear as if all the option arguments were specified before all the non-option arguments for the purposes of parsing, even if the user of your program intermixed option and non-option arguments. They do this by reordering the elements of the argv array. This behavior is nonstandard; if you want to suppress it, define the _POSIX_OPTION_ORDER environment variable. See Standard Environment, https://www.gnu.org/software/libc/manual/html_node/Standard-Environment.html.

  • The argument ‘--’ terminates all options; any following arguments are treated as non-option arguments, even if they begin with a hyphen.
  • A token consisting of a single hyphen character is interpreted as an ordinary non-option argument. By convention, it is used to specify input from or output to the standard input and output streams.
  • Options may be supplied in any order, or appear multiple times. The interpretation is left up to the particular application program. GNU adds long options to these conventions. Long options consist of ‘--’ followed by a name made of alphanumeric characters and dashes. Option names are typically one to three words long, with hyphens to separate words. Users can abbreviate the option names as long as the abbreviations are unique.

To specify an argument for a long option, write ‘--name=value’. This syntax enables a long option to accept an argument that is itself optional.

Eventually, GNU systems will provide completion for long option names in the shell.

About

This package is a fork of the Go standard library flag and gnuflag. As this package is a rewrite to enable additional functionality and usability, one will find it is significantly different from the source. The driving motivation was to provide a solution to the missing toolbox, an excellent flag parser that is simple and is very similar to other gnu programs. Being very similar to other gnu programs lowers the learning curve for users to use flags in go-built-tools. Modeled gnu programs used in the creation of this tool are the openldap and curl help flags.

Goals

This re-write includes some notable differences:

  • Support for both --longflag and -l single-character flag syntax.
  • Addition of "present" flag with no parameters needed.
  • Boolean flags always require a boolean input, true, t, 1, false, f, or 0 with either space ' ' or '=' separator.
  • Flag stacking -abc is the same as -a -b -c for present flags.
  • Unicode support for inputs and printing with alignment.
  • Multiple flags for a single target value -i, --include.
  • Custom exemplars demonstrating the needed input type
    --time DURATION   How long to wait for a reply.  (Default: 5s)
    
  • Custom definable functions to handle the parsing of parameters.
  • Ability to allow more than one input per parameter --port-range 1080 1090, by using the custom var and the needed count.
  • Collect a dynamic number of strings per flag into a slice, like multiple packages afer an --install flag.
    ctl --install pkgA pkgB pkgC --remove pkgX
    
  • Allow interspersed parameters. If set -a data -b is the same as -a -b data.

Example

Here is what it looks like when implemented:

import (
  ...
  "github.com/pschou/go-params"
  ...
)

var version = "0.0"
func main() {
  // Set a custom header,
  params.Usage = func() {
    fmt.Fprintf(os.Stderr, "My Sample, Version: %s\n\n" +
      "Usage: %s [options...]\n\n", version, os.Args[0])
    params.PrintDefaults()
  }

  // An example boolean flag, used like this: -tls true -tls false, or optionally: -tls=true -tls=false
  var tls_enabled = params.Bool("tls", true, "Enable listener TLS", "BOOL")

  // An example of a present flag, returns true if it was seen
  var verbose = params.Pres("debug", "Verbose output")

  // Start of a grouping set
  params.GroupingSet("Listener")
  var listen = params.String("listen", ":7443", "Listen address for forwarder", "HOST:PORT")
  var verify_server = params.Bool("verify-server", true, "Verify server, do certificate checks", "BOOL")
  var secure_server = params.Bool("secure-server", true, "Enforce minimum of TLS 1.2 on server side", "BOOL")

  // Start of another grouping set
  params.GroupingSet("Target")
  var target = params.String("target", "127.0.0.1:443", "Sending address for forwarder", "HOST:PORT")
  var verify_client = params.Bool("verify-client", true, "Verify client, do certificate checks", "BOOL")
  var secure_client = params.Bool("secure-client", true, "Enforce minimum of TLS 1.2 on client side", "BOOL")
  // To enable both -H and --host as options, all one needs to do is add a space "host" -> "host H"
  var tls_host = params.String("host", "", "Hostname to verify outgoing connection with", "FQDN")

  // Start of our last grouping set
  params.GroupingSet("Certificate")
  var cert_file = params.String("cert", "/etc/pki/server.pem", "File to load with CERT - automatically reloaded every minute\n", "FILE")
  var key_file = params.String("key", "/etc/pki/server.pem", "File to load with KEY - automatically reloaded every minute\n", "FILE")
  var root_file = params.String("ca", "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", "File to load with ROOT CAs - reloaded every minute by adding any new entries\n", "FILE")

  // Indicate that we want all the flags indented for ease of reading
  params.CommandLine.Indent = 2

  // Let us parse everything!
  params.Parse()

  // ... Variables are ready for use now!
}

This example was taken directly from the SSL-Forwarder program (below) so one may compare the output and see what it looks like in the finished product.

Real World Examples

Here are some examples which demonstrate the likeness of this parameter parsing tool:

SSL-Forwarder -- https://github.com/pschou/ssl-forwarder

$ ./ssl-forwarder -h
...
Usage: ./ssl-forwarder [options...]

Options:
  --debug                 Verbose output
  --tls BOOL              Enable listener TLS  (Default: true)
Listener options:
  --listen HOST:PORT      Listen address for forwarder  (Default: ":7443")
  --secure-server BOOL    Enforce minimum of TLS 1.2 on server side  (Default: true)
  --verify-server BOOL    Verify server, do certificate checks  (Default: true)
Target options:
  --host FQDN             Hostname to verify outgoing connection with  (Default: "")
  --secure-client BOOL    Enforce minimum of TLS 1.2 on client side  (Default: true)
  --target HOST:PORT      Sending address for forwarder  (Default: "127.0.0.1:443")
  --verify-client BOOL    Verify client, do certificate checks  (Default: true)
Certificate options:
  --ca FILE               File to load with ROOT CAs - reloaded every minute by adding any new entries
                            (Default: "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem")
  --cert FILE             File to load with CERT - automatically reloaded every minute
                            (Default: "/etc/pki/server.pem")
  --key FILE              File to load with KEY - automatically reloaded every minute
                            (Default: "/etc/pki/server.pem")

Prom-collector -- https://github.com/pschou/prom-collector

$ ./prom-collector -h
Prometheus Collector, written by Paul Schou (github.com/pschou/prom-collector) in December 2020
Prsonal use only, provided AS-IS -- not responsible for loss.
Usage implies agreement.

Usage: ./prom-collector [options...]

Options:
--ca FILE             File to load with ROOT CAs - reloaded every minute by adding any new entries
                        (Default: "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem")
--cert FILE           File to load with CERT - automatically reloaded every minute
                        (Default: "/etc/pki/server.pem")
--debug               Verbose output
--json JSON_FILE      Path into which to put all the prometheus endpoints for polling
                        (Default: "/dev/shm/metrics.json")
--key FILE            File to load with KEY - automatically reloaded every minute
                        (Default: "/etc/pki/server.pem")
--listen HOST:PORT    Listen address for metrics  (Default: ":9550")
--path DIRECTORY      Path into which to put the prometheus data  (Default: "/dev/shm/collector")
--prefix URL_PREFIX   Used for all incoming requests, useful for a reverse proxy endpoint
                        (Default: "/collector")
--secure-server BOOL  Enforce TLS 1.2+ on server side  (Default: true)
--tls BOOL            Enable listener TLS  (Default: false)
--verify-server BOOL  Verify or disable server certificate check  (Default: true)

jqURL -- https://github.com/pschou/jqURL

$ jqurl -h
jqURL - URL and JSON parser tool, Written by Paul Schou (github.com/pschou/jqURL)
Usage:
  ./jqurl [options] "JSON Parser" URLs

Options:
  -C, --cache          Use local cache to speed up static queries
      --cachedir DIR   Path for cache  (Default="/dev/shm")
      --debug          Debug / verbose output
      --flush          Force redownload, when using cache
  -i, --include        Include header in output
      --max-age DURATION  Max age for cache  (Default=4h0m0s)
  -o, --output FILE    Write output to <file> instead of stdout  (Default="")
  -P, --pretty         Pretty print JSON with indents
  -r, --raw-output     Raw output, no quotes for strings
Request options:
  -d, --data STRING    Data to use in POST (use @filename to read from file)  (Default="")
  -H, --header 'HEADER: VALUE'  Custom header to pass to server
                         (Default="content-type: application/json")
  -k, --insecure       Ignore certificate validation checks
  -L, --location       Follow redirects
  -m, --max-time DURATION  Timeout per request  (Default=15s)
      --max-tries TRIES  Maximum number of tries  (Default=30)
  -X, --request METHOD  Method to use for HTTP request (ie: POST/GET)  (Default="GET")
      --retry-delay DURATION  Delay between retries  (Default=7s)
Certificate options:
      --cacert FILE    Use certificate authorities, PEM encoded  (Default="")
  -E, --cert FILE      Use client cert in request, PEM encoded  (Default="")
      --key FILE       Key file for client cert, PEM encoded  (Default="")

Last, but not least, a test example using some unicode:

-A          for bootstrapping, allow 'any' type  (Default: false)
    --Alongflagname  disable bounds checking  (Default: false)
-C          a boolean defaulting to true  (Default: true)
-D          set relative path for local imports  (Default: "")
-E          issue 23543  (Default: "0")
-F STR      issue 23543  (Default: "0")
-I          a non-zero number  (Default: 2.7)
-K          a float that defaults to zero  (Default: 0)
-M          a multiline
            help
            string  (Default: "")
-N          a non-zero int  (Default: 27)
-O          a flag
            multiline help string  (Default: true)
-Z          an int that defaults to zero  (Default: 0)
-G, --grind STR  issue 23543  (Default: "0")
    --maxT  set timeout for dial  (Default: 0s)
-世         a present flag
    --世界  unicode string  (Default: "hello")

# Functions

Arg returns the i'th command-line argument.
Args returns the non-flag command-line arguments.
Bool defines a bool flag with specified name, default value, and usage string.
BoolVar defines a bool flag with specified name, default value, and usage string.
Duration defines a time.Duration flag with specified name, default value, and usage string.
DurationVar defines a time.Duration flag with specified name, default value, and usage string.
FlagFunc defines a flag with the specified name and usage string.
Float64 defines a float64 flag with specified name, default value, and usage string.
Float64Var defines a float64 flag with specified name, default value, and usage string.
GroupingSet creates a grouping set for new flags added.
Int defines an int flag with specified name, default value, and usage string.
Int64 defines an int64 flag with specified name, default value, and usage string.
Int64Var defines an int64 flag with specified name, default value, and usage string.
IntVar defines an int flag with specified name, default value, and usage string.
Lookup returns the Flag structure of the named command-line flag, returning nil if none exists.
NArg is the number of arguments remaining after flags have been processed.
NewFlagSet returns a new, empty parameter set with the specified name and error handling property.
NewFlagSetWithFlagKnownAs returns a new, empty parameter set with the specified name and error handling property.
NFlag returns the number of command-line flags that have been set.
Parse parses the command-line flags from os.Args[1:].
Parsed returns true if the command-line flags have been parsed.
Pres defines a present flag with specified name and usage string.
PresVar defines a present flag with specified name and usage string.
PrintDefaults prints to standard error the default values of all defined command-line flags.
Set sets the value of the named command-line flag.
SetAllowIntersperse tells the parser if flags can be interspersed with other arguments.
String defines a string flag with specified name, default value, and usage string.
StringSlice defines a string flag with specified name, default value, and usage string.
StringSliceVar defines a string flag with specified name, default value, and usage string.
StringVar defines a string flag with specified name, default value, and usage string.
Uint defines a uint flag with specified name, default value, and usage string.
Uint64 defines a uint64 flag with specified name, default value, and usage string.
Uint64Var defines a uint64 flag with specified name, default value, and usage string.
UintVar defines a uint flag with specified name, default value, and usage string.
Var defines a flag with the specified name and usage string.
Visit visits the command-line flags in lexicographical order, calling fn for each.
VisitAll visits the command-line flags in lexicographical order, calling fn for each.

# Constants

No description provided by the author
No description provided by the author
No description provided by the author

# Variables

CommandLine is the default set of command-line flags, parsed from os.Args.
Word for default.
ErrHelp is the error returned if the -help or -h flag is invoked but no such flag is defined.
Usage prints to standard error a usage message documenting all defined command-line flags.

# Structs

A Flag represents the state of a flag.
A FlagSet represents a set of defined flags.
No description provided by the author

# Interfaces

Getter is an interface that allows the contents of a Value to be retrieved.
Value is the interface to the dynamic value stored in a flag.

# Type aliases

ErrorHandling defines how to handle flag parsing errors.