Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bill #9

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
145 changes: 77 additions & 68 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,85 +1,94 @@
# conf
Package conf provides support for using environmental variables and command
line arguments for configuration.

Simple, self-documenting, struct-driven configuration with flag generation and zero dependencies.
It is compatible with the GNU extensions to the POSIX recommendations
for command-line options. See
http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html

## Overview
`conf` provides a simple method to drive structured configuration from types and fields, with automatic flag and usage generation.
There are no hard bindings for this package. This package takes a struct
value and parses it for both the environment and flags. It supports several tags
to customize the flag options.

## Usage
```go
package main
default - Provides the default value for the help
env - Allows for overriding the default variable name.
flag - Allows for overriding the default flag name.
short - Denotes a shorthand option for the flag.
noprint - Denotes to not include the field in any display string.
required - Denotes a value must be provided.
help - Provides a description for the help.

import (
"log"
"time"
The field name and any parent struct name will be used for the long form of
the command name unless the name is overridden.

"github.com/flowchartsman/conf"
)
As an example, this config struct:
```
type ip struct {
Name string `conf:"default:localhost,env:IP_NAME_VAR"`
IP string `conf:"default:127.0.0.0"`
}
type Embed struct {
Name string `conf:"default:bill"`
Duration time.Duration `conf:"default:1s,flag:e-dur,short:d"`
}
type config struct {
AnInt int `conf:"default:9"`
AString string `conf:"default:B,short:s"`
Bool bool
Skip string `conf:"-"`
IP ip
Embed
}
```
Would produce the following usage output:
```
Usage: conf.test [options] [arguments]

type myConfig struct {
Sub subConfig
TimeToWait time.Duration `conf:"help:how long to wait,short:c,required"`
Password string `conf:"help:the database password to use,noprint"`
DNSServer *string `conf:"help:the address of the dns server to use,default:127.0.0.1"`
Debug bool `conf:"help:enable debug mode"`
DBServers []string `conf:"help:a list of mirror 'host's to contact"`
}
OPTIONS
--an-int/$CRUD_AN_INT <int> (default: 9)
--a-string/-s/$CRUD_A_STRING <string> (default: B)
--bool/$CRUD_BOOL <bool>
--ip-name/$CRUD_IP_NAME_VAR <string> (default: localhost)
--ip-ip/$CRUD_IP_IP <string> (default: 127.0.0.0)
--name/$CRUD_NAME <string> (default: bill)
--e-dur/-d/$CRUD_DURATION <duration> (default: 1s)
--help/-h
display this help message
```

type subConfig struct {
Value int `conf:"help: I am a subvalue"`
}
The API is a single call to `Parse`
```
// Parse(args []string, namespace string, cfgStruct interface{}, sources ...Sourcer) error

func main() {
log.SetFlags(0)
var c myConfig
err := conf.Parse(&c,
conf.WithConfigFile("/etc/test.conf"),
conf.WithConfigFileFlag("conf"))
if err != nil {
log.Fatal(err)
if err := conf.Parse(os.Args, "CRUD", &cfg); err != nil {
log.Fatalf("main : Parsing Config : %v", err)
}
log.Println(conf.String(&c))
}
```

```
$ ./conftest -h
Usage: ./conftest [options] [arguments]
Additionally, if the config struct has a field of the slice type `conf.Args`
then it will be populated with any remaining arguments from the command line
after flags have been processed.

OPTIONS
--db-servers <host>,[host...] DB_SERVERS
a list of mirror hosts to contact
--debug enable debug mode DEBUG
--dns-server <string> DNS_SERVER
the address of the dns server to use
(default: 127.0.0.1)
--password <string> PASSWORD
the database password to use
(noprint)
--sub-value <int> SUB_VALUE
I am a subvalue
--time-to-wait, -c <duration> TIME_TO_WAIT
how long to wait
(required)
--conf filename
the filename to load configuration from
(default: /etc/test.conf)
--help, -h display this help message
For example a program with a config struct like this:

```
var cfg struct {
Port int
Args conf.Args
}
```

FILES
/etc/test.conf
The system-wide configuration file (overridden by --conf)
If that program is executed from the command line like this:

$ ./conftest
required field TimeToWait is missing value
$ ./conftest --time-to-wait 5s --sub-value 1 --password I4mInvisbl3! --db-servers 127.0.0.1,127.0.0.2 --dns-server 1.1.1.1
SUB_VALUE=1 TIME_TO_WAIT=5s DNS_SERVER=1.1.1.1 DEBUG=false DB_SERVERS=[127.0.0.1 127.0.0.2] <nil>
```
$ my-program --port=9000 serve http
```

## note
This library is still in **alpha**. It needs docs, full coverage testing, and poking to find edgecases.
Then the `cfg.Args` field will contain the string values `["serve", "http"]`.
The `Args` type has a method `Num` for convenient access to these arguments
such as this:

## shoulders
This library takes inspiration (and some code) from some great work by some great engineers. These are credited in the license, but more detail soon.
- [kelseyhightower/envconfig](https://github.com/kelseyhightower/envconfig)
- [peterbourgon/ff](https://github.com/peterbourgon/ff)
```
arg0 := cfg.Args.Num(0) // "serve"
arg1 := cfg.Args.Num(1) // "http"
arg2 := cfg.Args.Num(2) // "" empty string: not enough arguments
```
Loading