mirror of
https://github.com/rclone/rclone.git
synced 2024-11-28 19:34:55 +01:00
fs: add a tristate true/false/unset configuration value
This commit is contained in:
parent
1e4ef4b4d5
commit
9218a3eb00
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 New Issue
Block a user