aboutsummaryrefslogtreecommitdiff
path: root/internal/flagenv
diff options
context:
space:
mode:
authorChristine Dodrill <me@christine.website>2019-01-12 08:20:05 -0800
committerChristine Dodrill <me@christine.website>2019-01-12 08:20:13 -0800
commit481b85042540815aad18306ec1232e96357f9083 (patch)
tree2e3e70dd0ac23b4da7cc5dd1ef0c1bfa39a5fc1a /internal/flagenv
parent0209425f2448c66ba9f0f274f64e41a8c12c81b7 (diff)
downloadx-481b85042540815aad18306ec1232e96357f9083.tar.xz
x-481b85042540815aad18306ec1232e96357f9083.zip
flagenv, use it, legal reworkings
Diffstat (limited to 'internal/flagenv')
-rw-r--r--internal/flagenv/LICENSE30
-rw-r--r--internal/flagenv/flagenv.go67
-rw-r--r--internal/flagenv/flagenv_test.go82
-rw-r--r--internal/flagenv/legal.go36
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.`)
+}