@cryptotaxi247 / kubo / commits / dfe53cd5b

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 +}