diff options
| author | Christine Dodrill <me@christine.website> | 2019-01-12 08:20:05 -0800 |
|---|---|---|
| committer | Christine Dodrill <me@christine.website> | 2019-01-12 08:20:13 -0800 |
| commit | 481b85042540815aad18306ec1232e96357f9083 (patch) | |
| tree | 2e3e70dd0ac23b4da7cc5dd1ef0c1bfa39a5fc1a /internal/flagenv | |
| parent | 0209425f2448c66ba9f0f274f64e41a8c12c81b7 (diff) | |
| download | x-481b85042540815aad18306ec1232e96357f9083.tar.xz x-481b85042540815aad18306ec1232e96357f9083.zip | |
flagenv, use it, legal reworkings
Diffstat (limited to 'internal/flagenv')
| -rw-r--r-- | internal/flagenv/LICENSE | 30 | ||||
| -rw-r--r-- | internal/flagenv/flagenv.go | 67 | ||||
| -rw-r--r-- | internal/flagenv/flagenv_test.go | 82 | ||||
| -rw-r--r-- | internal/flagenv/legal.go | 36 |
4 files changed, 215 insertions, 0 deletions
diff --git a/internal/flagenv/LICENSE b/internal/flagenv/LICENSE new file mode 100644 index 0000000..bde9055 --- /dev/null +++ b/internal/flagenv/LICENSE @@ -0,0 +1,30 @@ +BSD License + +For flagenv software + +Copyright (c) 2015, Facebook, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Facebook nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file diff --git a/internal/flagenv/flagenv.go b/internal/flagenv/flagenv.go new file mode 100644 index 0000000..fe2ea8d --- /dev/null +++ b/internal/flagenv/flagenv.go @@ -0,0 +1,67 @@ +// Package flagenv provides the ability to populate flags from +// environment variables. +package flagenv + +import ( + "flag" + "fmt" + "log" + "os" + "strings" +) + +// Specify a prefix for environment variables. +var Prefix = "" + +func contains(list []*flag.Flag, f *flag.Flag) bool { + for _, i := range list { + if i == f { + return true + } + } + return false +} + +// ParseSet parses the given flagset. The specified prefix will be applied to +// the environment variable names. +func ParseSet(prefix string, set *flag.FlagSet) error { + var explicit []*flag.Flag + var all []*flag.Flag + set.Visit(func(f *flag.Flag) { + explicit = append(explicit, f) + }) + + var err error + set.VisitAll(func(f *flag.Flag) { + if err != nil { + return + } + all = append(all, f) + if !contains(explicit, f) { + name := strings.Replace(f.Name, ".", "_", -1) + name = strings.Replace(name, "-", "_", -1) + if prefix != "" { + name = prefix + name + } + name = strings.ToUpper(name) + val := os.Getenv(name) + if val != "" { + if ferr := f.Value.Set(val); ferr != nil { + err = fmt.Errorf("failed to set flag %q with value %q", f.Name, val) + } + } + } + }) + return err +} + +// Parse will set each defined flag from its corresponding environment +// variable . If dots or dash are presents in the flag name, they will be +// converted to underscores. +// +// If Parse fails, a fatal error is issued. +func Parse() { + if err := ParseSet(Prefix, flag.CommandLine); err != nil { + log.Fatalln(err) + } +} diff --git a/internal/flagenv/flagenv_test.go b/internal/flagenv/flagenv_test.go new file mode 100644 index 0000000..4ff958b --- /dev/null +++ b/internal/flagenv/flagenv_test.go @@ -0,0 +1,82 @@ +package flagenv_test + +import ( + "flag" + "fmt" + "os" + "regexp" + "strings" + "testing" + + "github.com/Xe/x/internal/flagenv" + "github.com/facebookgo/ensure" +) + +func named(t, v string) string { return strings.ToUpper(t + v) } + +func ExampleParse() { + var raz string + flag.StringVar(&raz, "raz-value", "bar", "set the raz") + + // override default flag value with value found in MY_RAZ_VALUE + flagenv.Prefix = "my_" + flagenv.Parse() + + // override value found in MY_RAZ_VALUE with command line flag value -raz-value=foo + flag.Parse() +} + +func TestNothingToDo(t *testing.T) { + const name = "TestNothingToDo" + s := flag.NewFlagSet(name, flag.PanicOnError) + s.String(named(name, "foo"), "", "") + ensure.Nil(t, flagenv.ParseSet(name, s)) +} + +func TestAFewFlags(t *testing.T) { + const name = "TestAFewFlags" + s := flag.NewFlagSet(name, flag.PanicOnError) + const foo = "42" + const bar = int(43) + fooActual := s.String("foo", "", "") + barActual := s.Int("bar", 0, "") + os.Setenv(named(name, "foo"), foo) + os.Setenv(named(name, "bar"), fmt.Sprint(bar)) + ensure.Nil(t, flagenv.ParseSet(name, s)) + ensure.DeepEqual(t, *fooActual, foo) + ensure.DeepEqual(t, *barActual, bar) +} + +func TestInvalidFlagValue(t *testing.T) { + const name = "TestInvalidFlagValue" + s := flag.NewFlagSet(name, flag.PanicOnError) + s.Int("bar", 0, "") + os.Setenv(named(name, "bar"), "a") + ensure.Err(t, flagenv.ParseSet(name, s), + regexp.MustCompile(`failed to set flag "bar" with value "a"`)) +} + +func TestReturnsFirstError(t *testing.T) { + const name = "TestReturnsFirstError" + s := flag.NewFlagSet(name, flag.PanicOnError) + s.Int("bar1", 0, "") + s.Int("bar2", 0, "") + os.Setenv(named(name, "bar1"), "a") + ensure.Err(t, flagenv.ParseSet(name, s), + regexp.MustCompile(`failed to set flag "bar1" with value "a"`)) +} + +func TestExplicitAreIgnored(t *testing.T) { + const name = "TestExplicitAreIgnored" + s := flag.NewFlagSet(name, flag.PanicOnError) + const bar = int(43) + barActual := s.Int("bar", 0, "") + s.Parse([]string{"-bar", fmt.Sprint(bar)}) + os.Setenv(named(name, "bar"), "44") + ensure.Nil(t, flagenv.ParseSet(name, s)) + ensure.DeepEqual(t, *barActual, bar) +} + +func TestGlobalParse(t *testing.T) { + flagenv.Parse() +} diff --git a/internal/flagenv/legal.go b/internal/flagenv/legal.go new file mode 100644 index 0000000..41bf559 --- /dev/null +++ b/internal/flagenv/legal.go @@ -0,0 +1,36 @@ +package flagenv + +import "go4.org/legal" + +func init() { + legal.RegisterLicense(`BSD License + +For flagenv software + +Copyright (c) 2015, Facebook, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Facebook nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.`) +} |
