aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrégoire Duchêne <gduchene@awhk.org>2022-12-04 00:00:47 +0000
committerGrégoire Duchêne <gduchene@awhk.org>2022-12-10 14:11:53 +0000
commit73519a3538720b2cbca59cde36f755a1e4068eae (patch)
tree07d2a2f548616859e930f017f18f50880dd4b653
parent1ec7c85d76814533a8e401f36a51a2448430d9bd (diff)
Add Feature and FlagFeature{,Var}
Feature is essentially a boolean flag that can be safely mutated at run time. FlagFeature{,Var} make Feature objects work with flags.
-rw-r--r--flag.go62
-rw-r--r--flag_test.go21
2 files changed, 83 insertions, 0 deletions
diff --git a/flag.go b/flag.go
index 4506683..fd60893 100644
--- a/flag.go
+++ b/flag.go
@@ -7,7 +7,9 @@ import (
"flag"
"fmt"
"os"
+ "strconv"
"strings"
+ "sync/atomic"
"time"
)
@@ -103,6 +105,41 @@ func InitFlagSet(fs *flag.FlagSet, env []string, cfg map[string]string, args []s
return err
}
+// Feature represent a code feature that can be enabled and disabled.
+//
+// Feature must not be copied after its first use.
+type Feature struct {
+ Name string
+
+ _ NoCopy
+ enabled int32
+}
+
+// FlagFeature creates a feature that, i.e. a boolean flag that can
+// potentially be changed at run time.
+func FlagFeature(fs *flag.FlagSet, name string, enabled bool, usage string) *Feature {
+ f := &Feature{Name: name}
+ if enabled {
+ f.enabled = 1
+ } else {
+ f.enabled = 0
+ }
+ FlagFeatureVar(fs, f, name, usage)
+ return f
+}
+
+func FlagFeatureVar(fs *flag.FlagSet, f *Feature, name, usage string) {
+ fs.Var(flagFeature{f}, name, usage)
+}
+
+func (f *Feature) Disable() { atomic.SwapInt32(&f.enabled, 0) }
+func (f *Feature) Enable() { atomic.SwapInt32(&f.enabled, 1) }
+func (f *Feature) Enabled() bool { return atomic.LoadInt32(&f.enabled) == 1 }
+
+func (f *Feature) String() string {
+ return fmt.Sprintf("%s (enabled: %t)", f.Name, f.Enabled())
+}
+
// ParseTime parses a string according to the time.RFC3339 format.
func ParseTime(s string) (time.Time, error) {
return time.Parse(time.RFC3339, s)
@@ -112,6 +149,31 @@ func ParseTime(s string) (time.Time, error) {
// value or an error.
type ParseFunc[T any] func(string) (T, error)
+type flagFeature struct{ *Feature }
+
+func (flagFeature) IsBoolFlag() bool { return true }
+func (flagFeature) MutableFlag() {}
+
+func (f flagFeature) Set(s string) error {
+ enable, err := strconv.ParseBool(s)
+ if err != nil {
+ return err
+ }
+ if enable {
+ f.Enable()
+ } else {
+ f.Disable()
+ }
+ return nil
+}
+
+func (f flagFeature) String() string {
+ if f.Enabled() {
+ return "true"
+ }
+ return "false"
+}
+
type flagValue[T any] struct {
Parse ParseFunc[T]
Value *T
diff --git a/flag_test.go b/flag_test.go
index 91c6caa..1cc9ba8 100644
--- a/flag_test.go
+++ b/flag_test.go
@@ -11,6 +11,18 @@ import (
"go.awhk.org/core"
)
+func TestFeature_Disable(t *testing.T) {
+ f := core.Feature{}
+ f.Disable()
+ (&core.T{T: t}).AssertEqual(false, f.Enabled())
+}
+
+func TestFeature_Enable(t *testing.T) {
+ f := core.Feature{}
+ f.Enable()
+ (&core.T{T: t}).AssertEqual(true, f.Enabled())
+}
+
func TestFlag(s *testing.T) {
t := core.T{T: s}
@@ -21,6 +33,15 @@ func TestFlag(s *testing.T) {
t.AssertEqual(84, *fl)
}
+func TestFlagFeature(s *testing.T) {
+ t := core.T{T: s}
+
+ fs := flag.NewFlagSet("", flag.PanicOnError)
+ ff := core.FlagFeature(fs, "some-feature", false, "")
+ t.AssertErrorIs(nil, fs.Parse([]string{"-some-feature"}))
+ t.AssertEqual(true, ff.Enabled())
+}
+
func TestFlagVar(s *testing.T) {
t := core.T{T: s}