fs: add a tristate true/false/unset configuration value
This commit is contained in:
parent
1e4ef4b4d5
commit
9218a3eb00
2 changed files with 161 additions and 0 deletions
74
fs/tristate.go
Normal file
74
fs/tristate.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Tristate is a boolean that can has the states, true, false and
|
||||
// unset/invalid/nil
|
||||
type Tristate struct {
|
||||
Value bool
|
||||
Valid bool
|
||||
}
|
||||
|
||||
// String renders the tristate as true/false/unset
|
||||
func (t Tristate) String() string {
|
||||
if !t.Valid {
|
||||
return "unset"
|
||||
}
|
||||
if t.Value {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
}
|
||||
|
||||
// Set the List entries
|
||||
func (t *Tristate) Set(s string) error {
|
||||
s = strings.ToLower(s)
|
||||
if s == "" || s == "nil" || s == "null" || s == "unset" {
|
||||
t.Valid = false
|
||||
return nil
|
||||
}
|
||||
value, err := strconv.ParseBool(s)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to parse Tristate %q", s)
|
||||
}
|
||||
t.Value = value
|
||||
t.Valid = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type of the value
|
||||
func (Tristate) Type() string {
|
||||
return "Tristate"
|
||||
}
|
||||
|
||||
// Scan implements the fmt.Scanner interface
|
||||
func (t *Tristate) Scan(s fmt.ScanState, ch rune) error {
|
||||
token, err := s.Token(true, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return t.Set(string(token))
|
||||
}
|
||||
|
||||
// UnmarshalJSON parses it as a bool or nil for unset
|
||||
func (t *Tristate) UnmarshalJSON(in []byte) error {
|
||||
var b *bool
|
||||
err := json.Unmarshal(in, &b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if b != nil {
|
||||
t.Valid = true
|
||||
t.Value = *b
|
||||
} else {
|
||||
t.Valid = false
|
||||
}
|
||||
return nil
|
||||
}
|
87
fs/tristate_test.go
Normal file
87
fs/tristate_test.go
Normal file
|
@ -0,0 +1,87 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Check it satisfies the interface
|
||||
var _ flagger = (*Tristate)(nil)
|
||||
|
||||
func TestTristateString(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
in Tristate
|
||||
want string
|
||||
}{
|
||||
{Tristate{}, "unset"},
|
||||
{Tristate{Valid: false, Value: false}, "unset"},
|
||||
{Tristate{Valid: false, Value: true}, "unset"},
|
||||
{Tristate{Valid: true, Value: false}, "false"},
|
||||
{Tristate{Valid: true, Value: true}, "true"},
|
||||
} {
|
||||
got := test.in.String()
|
||||
assert.Equal(t, test.want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTristateSet(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
in string
|
||||
want Tristate
|
||||
err bool
|
||||
}{
|
||||
{"", Tristate{Valid: false, Value: false}, false},
|
||||
{"nil", Tristate{Valid: false, Value: false}, false},
|
||||
{"null", Tristate{Valid: false, Value: false}, false},
|
||||
{"UNSET", Tristate{Valid: false, Value: false}, false},
|
||||
{"true", Tristate{Valid: true, Value: true}, false},
|
||||
{"1", Tristate{Valid: true, Value: true}, false},
|
||||
{"false", Tristate{Valid: true, Value: false}, false},
|
||||
{"0", Tristate{Valid: true, Value: false}, false},
|
||||
{"potato", Tristate{Valid: false, Value: false}, true},
|
||||
} {
|
||||
var got Tristate
|
||||
err := got.Set(test.in)
|
||||
if test.err {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTristateScan(t *testing.T) {
|
||||
var v Tristate
|
||||
n, err := fmt.Sscan(" true ", &v)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, n)
|
||||
assert.Equal(t, Tristate{Valid: true, Value: true}, v)
|
||||
}
|
||||
|
||||
func TestTristateUnmarshalJSON(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
in string
|
||||
want Tristate
|
||||
err bool
|
||||
}{
|
||||
{`null`, Tristate{}, false},
|
||||
{`true`, Tristate{Valid: true, Value: true}, false},
|
||||
{`false`, Tristate{Valid: true, Value: false}, false},
|
||||
{`potato`, Tristate{}, true},
|
||||
{``, Tristate{}, true},
|
||||
} {
|
||||
var got Tristate
|
||||
err := json.Unmarshal([]byte(test.in), &got)
|
||||
if test.err {
|
||||
require.Error(t, err, test.in)
|
||||
} else {
|
||||
require.NoError(t, err, test.in)
|
||||
}
|
||||
assert.Equal(t, test.want, got, test.in)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue