jsonflag

package module
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Apr 9, 2025 License: MIT Imports: 8 Imported by: 0

README

jsonflag

Go Report Card GoDoc

jsonflag is an almost drop in replacement for Go's flag package that supports configs (JSON/JSON5), environmental variables, and CLI options.

Values set by a higher precedence overwrite values set by a lower precedence. This makes testing using CLI or environmental variables easy.

Order of precedence for set config values :

  1. Command line flags (cli Example: --flag1=Flag1Value)
  2. Environmental Variables (env Example: FLAG2=Flag2value)
  3. JSON config values (json Example: {"flag3": "Flag3Value"})
  4. Default values set on flag (go Example: flag.StringVar(&config.Flag4, "Flag4Name", "Flag4DefaultValue", "Flag4Description"))

To overwrite a value, say on the config, a CLI parameter may be set FLAG1=Flag1EnvValue go run --flag2=flag2CliValue

Config Path

If not specified, the default path is config.json5 in the current working directory. The config path can be specified in two ways:

  1. Via command line argument (which takes precedence):
go run main.go --config=test_config.json
  1. Programmatically in your Go code:
jsonflag.Path = "test_config.json"

Installation

Go get

go get github.com/zamicol/jsonflag

and import:

import "github.com/zamicol/jsonflag"

Quick Example

Example config.json5 file:

{
"flag1": "jsonFlag1",
"flag2": "jsonFlag2",
"flag3": 3,  // Comments and trailing commas in JSON5 configs are recommended.  
}

Example Go setup:

// Config struct name (tag should) match the json key.  
type Config struct {
	Flag1 string
	Flag2 string
	Flag3 int
}

func main(){
	var config Config
	// `flag` is still from the standard library.
	flag.StringVar(&config.Flag1, "Flag1Name", "Flag1DefaultValue", "Flag1Description")
	flag.StringVar(&config.Flag2, "Flag2Name", "Flag2DefaultValue", "Flag2Description")
	flag.IntVar(&config.Flag3, "Flag3Name", 1, "Flag3Description")

	// Instead of `flag.parse`, use `jsonflag.Parse(&config)` which is the only line that must be different from using `flag` normally.  
	
	jsonflag.Parse(&config)
}

The default values will be overwritten by the values in the JSON config, which may further be overwritten by environmental variables and CLI parameters. For example

Example overwriting values using environmental variable and command line options:

FLAG2=Flag2EnvValue go run main.go --flag1=Flag1CLIValue

Which will result in the final values being used:

Flag1 = Flag1CLIValue  // From command line flag. 
Flag2 = Flag2EnvValue // From environmental variable. 
Flag3 = 3 // From JSON file. 

See also the godocs for more complete documentation and a working example.

Documentation

Overview

Package jsonflag is for configuration settings. It extends Go's flag package, designed for cli flags, with json config files and environmental variables. It does not replace any part of flag.

Order of precedence for set config values:

  1. Command line flags. (cli Example: `--flag1=flag1Value`)
  2. Environmental Variables (env Example: FLAG2=flag2value)
  3. JSON config values. (json Example: `{"flag3": "flag3Value"}`)
  4. Default values set on flag. (go Example: `flag.StringVar(&config.Flag4, "flag4Name", "flag4DefaultValue", "flag4Description")`)

Flag values do not need to appear in the json config file and can be left blank if desired. If not set in config struct or exported, extra json config file values will be ignored. If a value should not be set by flags, add the value in the config struct and json config file. It will still be set.

Environmental variables can be set using the flag name. The flag's name will be converted to all upper case. If set, "EnvPrefix" will be prefixed when looking up environment variables.

Environmental variables in json config will be expanded. See testing for an example where "$Flag8" is expanded.

This package uses Go's json package for decoding. The json decoder only has accesses to exported fields of structs and follows its own precedence for json decoding:

  1. Tags
  2. Exact case
  3. Case insensitive

CLI names must start with lower case.

See testing for an example.

  1. Define a `config` struct with exported fields.
  2. Use flag's functions to set default config values. `flag.StringVar(&config.Flag1, "flag1Name", "flag1DefaultValue", "flag1Description")`
  3. Put config values in a `config.json` file. The config file path defaults to the cwd. You can use `--config=your_config.json` to point somewhere else.
  4. Call `jsonflag.Parse(&config)`

The config struct is now appropriately populated.

Config Path

You can set the config path via the cli,

--config=your_config.json

You can also set it in your application. Note that this can be overwritten by the normal precedence via a cli flag as previously mentioned.

jsonflag.Path = "assets/config.json"

Design

This package follows flag.Parse() fail fast design and panics on error.

Testing

Since this package uses flag, test functions need a cli flag passed to verify cli parsing is working. Test will otherwise fail.

Test 1 (tests --config= form):

JSONFLAG_FLAG10=FLAG10VALUE FLAG7=FLAG7VALUE Flag8=Flag8Env go test --flag1=cliFlag1 --config=test_config.json5

Test 2 (tests -config= form):

JSONFLAG_FLAG10=FLAG10VALUE FLAG7=FLAG7VALUE Flag8=Flag8Env go test --flag1=cliFlag1 -config=test_config.json5

Test 3 (tests --config form):

JSONFLAG_FLAG10=FLAG10VALUE FLAG7=FLAG7VALUE Flag8=Flag8Env go test --flag1=cliFlag1 --config test_config.json5

Test 4 (tests -config form):

JSONFLAG_FLAG10=FLAG10VALUE FLAG7=FLAG7VALUE Flag8=Flag8Env go test --flag1=cliFlag1 -config test_config.json5

Example

Example prints out values

package main

import (
	"flag"
	"fmt"
)

// Create two new configs.  Typical usage uses only one config.
var tc Config
var tc2 Config2

// Config's values must be exported
type Config struct {
	Flag1 string // Set by flag default, JSON, and CLI - CLI precedence
	Flag2 string // Set by JSON only - JSON precedence
	Flag3 string // Set by flag default only - default precedence
	Flag4 string // Set by JSON and flag default - JSON precedence
	Flag5 int    // Set by JSON only int - JSON precedence
	Flag6 int    `json:"flagsix"` // JSON with flag default int - JSON precedence.  Also tests tags.
	Flag7 string // Set by environmental variable and JSON.  No default, or CLI. - Env precedence
	Flag8 string // Set by environmental value expansion from value in JSON config.
	Flag9 string // Test expanding the default flag value ($FLAG7) with a variable to an environmental variable."
}

type Config2 struct {
	Flag10 string // Test EnvPrefix.  Value is set by CLI only.
}

// flags holds all flag definitions for CLI and application set.
// Flag 2 and 5 are missing to test JSON values which will still populate.
func flags() {
	flag.StringVar(&tc.Flag1, "flag1", "defaultFlag1", "flag1Desc")
	flag.StringVar(&tc.Flag3, "flag3", "defaultFlag3", "flag3Desc")
	flag.StringVar(&tc.Flag4, "flag4", "defaultFlag4", "flag4Desc")
	flag.IntVar(&tc.Flag6, "flag6", 1, "flag6Desc") // Set default value to something other than 6 for testing.
	flag.StringVar(&tc.Flag7, "flag7", "defaultFlag7", "Flag7's value comes from environmental variable.")
	flag.StringVar(&tc.Flag8, "flag8", "defaultFlag8", "Flag8 tests environmental expansion.")
	flag.StringVar(&tc.Flag9, "flag9", "$FLAG7", "Flag9's value comes from expanding the default flag value ($FLAG7) with a variable to an environmental variable.")
	Parse(&tc)

	EnvPrefix = "JSONFLAG_"
	flag.StringVar(&tc2.Flag10, "flag10", "", "Flag10 tests prefixing the EnvPrefix to env vars")
	Parse(&tc2)
}

// Example prints out values
func main() {
	fmt.Println(tc)
	fmt.Println(tc2)
}
Output:

{cliFlag1 jsonFlag2 defaultFlag3 jsonFlag4 5 6 FLAG7VALUE Flag8Env FLAG7VALUE}
{FLAG10VALUE}

Index

Examples

Constants

This section is empty.

Variables

View Source
var EnvPrefix = ""

EnvPrefix will be prepended to flag names if set. For example, with a prefix of "MYAPP_", the flag "flag1" will become "MYAPP_FLAG1".

View Source
var Path string

Path defines the default config path and is relative to pwd.

Functions

func Parse

func Parse(c interface{})

Parse reads the config file and parses CLI flags into c with a single flag.Parse() call.

Types

This section is empty.

Jump to

Keyboard shortcuts

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