go-ipfs-config: feat: add flag and priority types
These let us zero-encode "default" to "null" (and omit it with "omitempty") so we don't have to hard code the default in the config.
Steven Allen committed
Jun 15, 2020 at 13:17 UTC
dfe53cd5b62fff8d81f74bb6a33ab4d91d9e398c
2 files changed
+226
config/types.go
+127
@@ -1,7 +1,9 @@
1
package config
2
3
import (
4
+ "encoding"
5
"encoding/json"
6
+ "fmt"
7
"time"
8
)
9
@@ -41,6 +43,128 @@ func (o Strings) MarshalJSON() ([]byte, error) {
43
var _ json.Unmarshaler = (*Strings)(nil)
44
var _ json.Marshaler = (*Strings)(nil)
45
46
+// Flag represents a ternary value: false (-1), default (0), or true (+1).
47
+//
48
+// When encoded in json, False is "false", Default is "null" (or empty), and True
49
+// is "true".
50
+type Flag int8
51
+
52
+const (
53
+ False Flag = -1
54
+ Default Flag = 0
55
+ True Flag = 1
56
+)
57
+
58
+func (f Flag) MarshalJSON() ([]byte, error) {
59
+ switch f {
60
+ case Default:
61
+ return json.Marshal(nil)
62
+ case True:
63
+ return json.Marshal(true)
64
+ case False:
65
+ return json.Marshal(false)
66
+ default:
67
+ return nil, fmt.Errorf("invalid flag value: %d", f)
68
+ }
69
+}
70
+
71
+func (f *Flag) UnmarshalJSON(input []byte) error {
72
+ switch string(input) {
73
+ case "null", "undefined":
74
+ *f = Default
75
+ case "false":
76
+ *f = False
77
+ case "true":
78
+ *f = True
79
+ default:
80
+ return fmt.Errorf("failed to unmarshal %q into a flag: must be null/undefined, true, or false", string(input))
81
+ }
82
+ return nil
83
+}
84
+
85
+func (f Flag) String() string {
86
+ switch f {
87
+ case Default:
88
+ return "default"
89
+ case True:
90
+ return "true"
91
+ case False:
92
+ return "false"
93
+ default:
94
+ return fmt.Sprintf("<invalid flag value %d>", f)
95
+ }
96
+}
97
+
98
+var _ json.Unmarshaler = (*Flag)(nil)
99
+var _ json.Marshaler = (*Flag)(nil)
100
+
101
+// Priority represents a value with a priority where 0 means "default" and -11
102
+// means "disabled".
103
+//
104
+// When encoded in json, Default is encoded as "null" and Disabled is encoded as
105
+// "false".
106
+type Priority int64
107
+
108
+const (
109
+ DefaultPriority Priority = 0
110
+ Disabled Priority = -1
111
+)
112
+
113
+func (p Priority) MarshalJSON() ([]byte, error) {
114
+ // > 0 == Priority
115
+ if p > 0 {
116
+ return json.Marshal(int64(p))
117
+ }
118
+ // <= 0 == special
119
+ switch p {
120
+ case DefaultPriority:
121
+ return json.Marshal(nil)
122
+ case Disabled:
123
+ return json.Marshal(false)
124
+ default:
125
+ return nil, fmt.Errorf("invalid priority value: %d", p)
126
+ }
127
+}
128
+
129
+func (p *Priority) UnmarshalJSON(input []byte) error {
130
+ switch string(input) {
131
+ case "null", "undefined":
132
+ *p = DefaultPriority
133
+ case "false":
134
+ *p = Disabled
135
+ case "true":
136
+ return fmt.Errorf("'true' is not a valid priority")
137
+ default:
138
+ var priority int64
139
+ err := json.Unmarshal(input, &priority)
140
+ if err != nil {
141
+ return err
142
+ }
143
+ if priority <= 0 {
144
+ return fmt.Errorf("priority must be positive: %d <= 0", priority)
145
+ }
146
+ *p = Priority(priority)
147
+ }
148
+ return nil
149
+}
150
+
151
+func (p Priority) String() string {
152
+ if p > 0 {
153
+ return fmt.Sprintf("%d", p)
154
+ }
155
+ switch p {
156
+ case DefaultPriority:
157
+ return "default"
158
+ case Disabled:
159
+ return "false"
160
+ default:
161
+ return fmt.Sprintf("<invalid priority %d>", p)
162
+ }
163
+}
164
+
165
+var _ json.Unmarshaler = (*Flag)(nil)
166
+var _ json.Marshaler = (*Flag)(nil)
167
+
168
// Duration wraps time.Duration to provide json serialization and deserialization.
169
//
170
// NOTE: the zero value encodes to an empty string.
@@ -59,3 +183,6 @@ func (d Duration) MarshalText() ([]byte, error) {
183
func (d Duration) String() string {
184
return time.Duration(d).String()
185
}
186
+
187
+var _ encoding.TextUnmarshaler = (*Duration)(nil)
188
+var _ encoding.TextMarshaler = (*Duration)(nil)
config/types_test.go
+99
@@ -83,3 +83,102 @@ func TestFunkyStrings(t *testing.T) {
83
t.Fatalf("unexpected result: %v", s)
84
}
85
}
86
+
87
+func TestFlag(t *testing.T) {
88
+ // make sure we have the right zero value.
89
+ var defaultFlag Flag
90
+ if defaultFlag != Default {
91
+ t.Errorf("expected default flag to be %q, got %q", Default, defaultFlag)
92
+ }
93
+
94
+ for jsonStr, goValue := range map[string]Flag{
95
+ "null": Default,
96
+ "true": True,
97
+ "false": False,
98
+ } {
99
+ var d Flag
100
+ err := json.Unmarshal([]byte(jsonStr), &d)
101
+ if err != nil {
102
+ t.Fatal(err)
103
+ }
104
+ if d != goValue {
105
+ t.Fatalf("expected %s, got %s", goValue, d)
106
+ }
107
+
108
+ // Reverse
109
+ out, err := json.Marshal(goValue)
110
+ if err != nil {
111
+ t.Fatal(err)
112
+ }
113
+ if string(out) != jsonStr {
114
+ t.Fatalf("expected %s, got %s", jsonStr, string(out))
115
+ }
116
+ }
117
+
118
+ type Foo struct {
119
+ F Flag `json:",omitempty"`
120
+ }
121
+ out, err := json.Marshal(new(Foo))
122
+ if err != nil {
123
+ t.Fatal(err)
124
+ }
125
+ expected := "{}"
126
+ if string(out) != expected {
127
+ t.Fatal("expected omitempty to omit the flag")
128
+ }
129
+}
130
+
131
+func TestPriority(t *testing.T) {
132
+ // make sure we have the right zero value.
133
+ var defaultPriority Priority
134
+ if defaultPriority != DefaultPriority {
135
+ t.Errorf("expected default priority to be %q, got %q", DefaultPriority, defaultPriority)
136
+ }
137
+
138
+ for jsonStr, goValue := range map[string]Priority{
139
+ "null": DefaultPriority,
140
+ "false": Disabled,
141
+ "1": 1,
142
+ "2": 2,
143
+ "100": 100,
144
+ } {
145
+ var d Priority
146
+ err := json.Unmarshal([]byte(jsonStr), &d)
147
+ if err != nil {
148
+ t.Fatal(err)
149
+ }
150
+ if d != goValue {
151
+ t.Fatalf("expected %s, got %s", goValue, d)
152
+ }
153
+
154
+ // Reverse
155
+ out, err := json.Marshal(goValue)
156
+ if err != nil {
157
+ t.Fatal(err)
158
+ }
159
+ if string(out) != jsonStr {
160
+ t.Fatalf("expected %s, got %s", jsonStr, string(out))
161
+ }
162
+ }
163
+
164
+ type Foo struct {
165
+ P Priority `json:",omitempty"`
166
+ }
167
+ out, err := json.Marshal(new(Foo))
168
+ if err != nil {
169
+ t.Fatal(err)
170
+ }
171
+ expected := "{}"
172
+ if string(out) != expected {
173
+ t.Fatal("expected omitempty to omit the flag")
174
+ }
175
+ for _, invalid := range []string{
176
+ "0", "-1", "-2", "1.1", "0.0",
177
+ } {
178
+ var p Priority
179
+ err := json.Unmarshal([]byte(invalid), &p)
180
+ if err == nil {
181
+ t.Errorf("expected to fail to decode %s as a priority", invalid)
182
+ }
183
+ }
184
+}