Skip to content
This repository was archived by the owner on Mar 9, 2022. It is now read-only.

Commit 16af6f8

Browse files
committed
refactor: use null in JSON
1 parent c9b7984 commit 16af6f8

File tree

2 files changed

+51
-45
lines changed

2 files changed

+51
-45
lines changed

Diff for: types.go

+13-12
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package config
22

33
import (
4-
"encoding"
54
"encoding/json"
65
"fmt"
6+
"strings"
77
"time"
88
)
99

@@ -213,18 +213,19 @@ var _ json.Marshaler = (*Priority)(nil)
213213

214214
// Duration wraps time.Duration to provide json serialization and deserialization.
215215
//
216-
// NOTE: the zero value encodes to an empty string.
216+
// NOTE: the zero value encodes to "default" string.
217217
type Duration struct {
218218
value *time.Duration
219219
}
220220

221-
func (d *Duration) UnmarshalText(text []byte) error {
222-
switch string(text) {
223-
case "null", "undefined", "":
221+
func (d *Duration) UnmarshalJSON(input []byte) error {
222+
switch string(input) {
223+
case "null", "undefined", "\"null\"", "", "default", "\"\"", "\"default\"":
224224
*d = Duration{}
225225
return nil
226226
default:
227-
value, err := time.ParseDuration(string(text))
227+
text := strings.Trim(string(input), "\"")
228+
value, err := time.ParseDuration(text)
228229
if err != nil {
229230
return err
230231
}
@@ -244,11 +245,11 @@ func (d *Duration) WithDefault(defaultValue time.Duration) time.Duration {
244245
return *d.value
245246
}
246247

247-
func (d Duration) MarshalText() ([]byte, error) {
248-
if d.value != nil {
249-
return []byte(d.value.String()), nil
248+
func (d Duration) MarshalJSON() ([]byte, error) {
249+
if d.value == nil {
250+
return json.Marshal("default")
250251
}
251-
return json.Marshal(nil)
252+
return json.Marshal(d.value.String())
252253
}
253254

254255
func (d Duration) String() string {
@@ -258,8 +259,8 @@ func (d Duration) String() string {
258259
return d.value.String()
259260
}
260261

261-
var _ encoding.TextUnmarshaler = (*Duration)(nil)
262-
var _ encoding.TextMarshaler = (*Duration)(nil)
262+
var _ json.Unmarshaler = (*Duration)(nil)
263+
var _ json.Marshaler = (*Duration)(nil)
263264

264265
// OptionalInteger represents an integer that has a default value
265266
//

Diff for: types_test.go

+38-33
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package config
22

33
import (
4+
"bytes"
45
"encoding/json"
56
"testing"
67
"time"
@@ -29,13 +30,13 @@ func TestDuration(t *testing.T) {
2930
})
3031

3132
t.Run("default value", func(t *testing.T) {
32-
for _, jsonStr := range []string{"null", "\"\"", "\"null\""} {
33+
for _, jsonStr := range []string{"null", "\"\"", "\"default\""} {
3334
var d Duration
3435
if !d.IsDefault() {
3536
t.Fatal("expected value to be the default initially")
3637
}
3738
if err := json.Unmarshal([]byte(jsonStr), &d); err != nil {
38-
t.Fatal(err)
39+
t.Fatalf("%s failed to unmarshall with %s", jsonStr, err)
3940
}
4041
if dur := d.WithDefault(time.Hour); dur != time.Hour {
4142
t.Fatalf("expected default value to be used, got %s", dur)
@@ -59,41 +60,45 @@ func TestDuration(t *testing.T) {
5960
}
6061
})
6162

62-
for jsonStr, goValue := range map[string]Duration{
63-
"\"\"": {},
64-
"null": {},
65-
"\"null\"": {},
66-
"\"1s\"": {value: makeDurationPointer(time.Second)},
67-
"\"42h1m3s\"": {value: makeDurationPointer(42*time.Hour + 1*time.Minute + 3*time.Second)},
68-
} {
69-
var d Duration
70-
err := json.Unmarshal([]byte(jsonStr), &d)
71-
if err != nil {
72-
t.Fatal(err)
73-
}
63+
t.Run("roundtrip including the default values", func(t *testing.T) {
64+
for jsonStr, goValue := range map[string]Duration{
65+
// there are various footguns user can hit, normalize them to the canonical default
66+
"null": {}, // JSON null → default value
67+
"\"null\"": {}, // JSON string "null" sent/set by "ipfs config" cli → default value
68+
"\"default\"": {}, // explicit "default" as string
69+
"\"\"": {}, // user removed custom value, empty string should also parse as default
70+
"\"1s\"": {value: makeDurationPointer(time.Second)},
71+
"\"42h1m3s\"": {value: makeDurationPointer(42*time.Hour + 1*time.Minute + 3*time.Second)},
72+
} {
73+
var d Duration
74+
err := json.Unmarshal([]byte(jsonStr), &d)
75+
if err != nil {
76+
t.Fatal(err)
77+
}
7478

75-
if goValue.value == nil && d.value == nil {
76-
} else if goValue.value == nil && d.value != nil {
77-
t.Errorf("expected nil for %s, got %s", jsonStr, d)
78-
} else if *d.value != *goValue.value {
79-
t.Fatalf("expected %s for %s, got %s", goValue, jsonStr, d)
80-
}
79+
if goValue.value == nil && d.value == nil {
80+
} else if goValue.value == nil && d.value != nil {
81+
t.Errorf("expected nil for %s, got %s", jsonStr, d)
82+
} else if *d.value != *goValue.value {
83+
t.Fatalf("expected %s for %s, got %s", goValue, jsonStr, d)
84+
}
8185

82-
// Test Reverse
83-
out, err := json.Marshal(goValue)
84-
if err != nil {
85-
t.Fatal(err)
86-
}
87-
if goValue.value == nil {
88-
if string(out) != "\"null\"" {
89-
t.Fatalf("expected null string for %s, got %s", jsonStr, string(out))
86+
// Test Reverse
87+
out, err := json.Marshal(goValue)
88+
if err != nil {
89+
t.Fatal(err)
90+
}
91+
if goValue.value == nil {
92+
if !bytes.Equal(out, []byte("\"default\"")) {
93+
t.Fatalf("expected default string for %s, got %s", jsonStr, string(out))
94+
}
95+
continue
96+
}
97+
if string(out) != jsonStr {
98+
t.Fatalf("expected %s, got %s", jsonStr, string(out))
9099
}
91-
continue
92-
}
93-
if string(out) != jsonStr {
94-
t.Fatalf("expected %s, got %s", jsonStr, string(out))
95100
}
96-
}
101+
})
97102
}
98103

99104
func TestOneStrings(t *testing.T) {

0 commit comments

Comments
 (0)