Skip to content
This repository has been archived by the owner on Dec 15, 2024. It is now read-only.

Commit

Permalink
env: add support for *Or parsing options for setting fallback values (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
shoenig authored Jul 14, 2023
1 parent 0a84aae commit 203359a
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 1 deletion.
71 changes: 70 additions & 1 deletion env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/shoenig/go-conceal"
)

// A Variable represents a set environment variable
// A Variable represents an environment variable.
type Variable string

func (v Variable) String() string {
Expand All @@ -20,12 +20,20 @@ func (v Variable) Name() string {
return string(v)
}

// Schema is used to describe how to parse a set of environment variables.
type Schema map[Variable]Parser

// ParseOS is a convenience function for parsing the given Schema of environment
// variables using the environment variables accessed by the standard libraray
// os package. If the values of environment variables do not match the schema,
// or required variables are missing, an error is returned.
func ParseOS(schema Schema) error {
return Parse(OS, schema)
}

// Parse uses the given Schema to parse the environment variables in the given
// Environment. If the values of environment variables in Environment do not
// match the schema, or required variables are missing, an error is returned.
func Parse(environment Environment, schema Schema) error {
for key, parser := range schema {
value := environment.Getenv(key.Name())
Expand All @@ -36,6 +44,8 @@ func Parse(environment Environment, schema Schema) error {
return nil
}

// The Parser interface is what must be implemented to support decoding an
// environment variable into a custom type.
type Parser interface {
Parse(string) error
}
Expand All @@ -56,13 +66,27 @@ func (sp *stringParser) Parse(s string) error {
return nil
}

// String is used to extract an environment variable into a Go string. If
// required is true, then an error is returned if the environment variable is
// not set or is empty.
func String(s *string, required bool) Parser {
return &stringParser{
required: required,
destination: s,
}
}

// StringOr is used to extract an environment variable into a Go string. If
// the environment variable is not set or is empty, then the alt value is used
// instead.
func StringOr(s *string, alt string) Parser {
*s = alt
return &stringParser{
required: false,
destination: s,
}
}

type secretParser struct {
required bool
destination **conceal.Text
Expand All @@ -80,6 +104,9 @@ func (sp *secretParser) Parse(s string) error {
return nil
}

// Secret is used to extract an environment variable into a Go concel.Text
// object. If required is true, then an error is returned if the environment
// variable is not set or is empty.
func Secret(s **conceal.Text, required bool) Parser {
return &secretParser{
required: required,
Expand Down Expand Up @@ -107,13 +134,27 @@ func (ip *intParser) Parse(s string) error {
return nil
}

// Int is used to extract an environment variable into a Go int. If required
// is true, then an error is returned if the environment variable is not set or
// is empty.
func Int(i *int, required bool) Parser {
return &intParser{
required: required,
destination: i,
}
}

// IntOr is used to extract an environment variable into a Go int. If the
// environment variable is not set or is empty, then the alt value is used
// instead.
func IntOr(i *int, alt int) Parser {
*i = alt
return &intParser{
required: false,
destination: i,
}
}

type floatParser struct {
required bool
destination *float64
Expand All @@ -134,13 +175,27 @@ func (fp *floatParser) Parse(s string) error {
return nil
}

// Float is used to extract an environment variable into a Go float64. If
// required is true, then an error is returned if the environment variable is
// not set or is empty.
func Float(f *float64, required bool) Parser {
return &floatParser{
required: required,
destination: f,
}
}

// FloatOr is used to extract an environment variable into a Go float64. If
// the environment variable is not set or is emty, then the alt value is used
// instead.
func FloatOr(f *float64, alt float64) Parser {
*f = alt
return &floatParser{
required: false,
destination: f,
}
}

type boolParser struct {
required bool
destination *bool
Expand All @@ -161,13 +216,27 @@ func (bp *boolParser) Parse(s string) error {
return nil
}

// Bool is used to extract an environment variable into a Go bool. If required
// is true, then an error is returned if the environment variable is not set or
// is empty.
func Bool(b *bool, required bool) Parser {
return &boolParser{
required: required,
destination: b,
}
}

// BoolOr is used to extract an environment variable into a Go bool. If the
// environment variable is not set or is empty, then the alt value is used
// instead.
func BoolOr(b *bool, alt bool) Parser {
*b = alt
return &boolParser{
required: false,
destination: b,
}
}

// Environment is something that implements Getenv().
//
// Most use cases can simply make use of the OS implementation which is backed
Expand Down
43 changes: 43 additions & 0 deletions env/env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,49 @@ func Test_Parse_required(t *testing.T) {
must.Eq(t, "hunter2", pass.Unveil())
}

func Test_ParseOr(t *testing.T) {
t.Setenv("FOO", "foo")
t.Setenv("BAR", "")
t.Setenv("I1", "42")
t.Setenv("I2", "")
t.Setenv("F1", "1.1")
t.Setenv("F2", "")
t.Setenv("B1", "true")
t.Setenv("B2", "")

var (
foo string
bar string
i1 int
i2 int
f1 float64
f2 float64
b1 bool
b2 bool
)

err := Parse(OS, Schema{
"FOO": StringOr(&foo, "baz"),
"BAR": StringOr(&bar, "baz"),
"I1": IntOr(&i1, 77),
"I2": IntOr(&i2, 77),
"F1": FloatOr(&f1, 50.5),
"F2": FloatOr(&f2, 50.5),
"B1": BoolOr(&b1, false),
"B2": BoolOr(&b2, true),
})

must.NoError(t, err)
must.Eq(t, "foo", foo)
must.Eq(t, "baz", bar)
must.Eq(t, 42, i1)
must.Eq(t, 77, i2)
must.Eq(t, f1, 1.1)
must.Eq(t, f2, 50.5)
must.True(t, b1)
must.True(t, b2)
}

func Test_Parse_optional(t *testing.T) {
t.Setenv("FOO", "")
t.Setenv("BAR", "")
Expand Down

0 comments on commit 203359a

Please sign in to comment.