-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add JSON-marshalable duration (#381)
- Loading branch information
1 parent
1a169f7
commit 42f7f8e
Showing
3 changed files
with
106 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package utils | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"time" | ||
) | ||
|
||
// Duration is a custom duration type that supports marshalling/unmarshalling. | ||
// This type and supporting functionality can be removed once go supports for | ||
// [time.Duration], which is planned for go2: https://github.com/golang/go/issues/10275 | ||
type Duration time.Duration | ||
|
||
// MarshalJSON marshals a [Duration] into JSON. | ||
func (d Duration) MarshalJSON() ([]byte, error) { | ||
return json.Marshal(time.Duration(d).String()) | ||
} | ||
|
||
// UnmarshalJSON unmarshals JSON data into a [Duration]. | ||
func (d *Duration) UnmarshalJSON(b []byte) error { | ||
var v interface{} | ||
if err := json.Unmarshal(b, &v); err != nil { | ||
return err | ||
} | ||
switch value := v.(type) { | ||
case string: | ||
tmp, err := time.ParseDuration(value) | ||
if err != nil { | ||
return err | ||
} | ||
*d = Duration(tmp) | ||
return nil | ||
default: | ||
return errors.New("invalid duration") | ||
} | ||
} | ||
|
||
// Unwrap converts a custom [Duration] into a native [time.Duration]. | ||
func (d Duration) Unwrap() time.Duration { | ||
return time.Duration(d) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package utils_test | ||
|
||
import ( | ||
"encoding/json" | ||
"testing" | ||
"time" | ||
|
||
"go.viam.com/test" | ||
|
||
"go.viam.com/utils" | ||
) | ||
|
||
func FuzzDurationJSONRoundtrip(f *testing.F) { | ||
f.Add("not a duration") | ||
f.Add("2h") | ||
f.Add("1m") | ||
f.Add("5s") | ||
f.Add("12ms") | ||
f.Add("8us") | ||
f.Add("600ns") | ||
f.Add("1h2m10s") | ||
f.Add("`0`") | ||
f.Add("5") | ||
f.Fuzz(func(t *testing.T, s string) { | ||
// marshal input to JSON. this should always succeed. | ||
data, err := json.Marshal(s) | ||
test.That(t, err, test.ShouldBeNil) | ||
|
||
// unmarshal marshaled input directly to custom [utils.Duration]. | ||
var ud utils.Duration | ||
errUD := json.Unmarshal(data, &ud) | ||
|
||
// parse input to built-in [time.Duration] | ||
td, errTD := time.ParseDuration(s) | ||
|
||
// the previous two marshall/parse operations should either both | ||
// succeed or both fail. | ||
if errUD != nil || errTD != nil { | ||
test.That(t, errUD, test.ShouldNotBeNil) | ||
test.That(t, errTD, test.ShouldNotBeNil) | ||
return | ||
} | ||
|
||
// if unmarshaling/parsing is successful, both durations should be equal. | ||
test.That(t, ud.Unwrap(), test.ShouldEqual, td) | ||
|
||
// marshal custom [util.Duration] value back to JSON. this should always succeed. | ||
// note that the resulting JSON value might not match initially marshaled input string. | ||
// the marshaling function for [util.Duration] is using [time.Duration.String], which | ||
// might "pad" the resulting string with zero values duration types. | ||
// for example, the following marshaling roundtrip is possible: | ||
// `"2h"` -> 2 * time.Hour -> `"2h0m0s""`. | ||
jsonUD, err := json.Marshal(ud) | ||
test.That(t, err, test.ShouldBeNil) | ||
|
||
// stringify and marshal built-in [time.Duration] value to JSON. | ||
// this should always succeed and match the result of marshaling the custom | ||
// [util.Duration] value. | ||
jsonTD, err := json.Marshal(td.String()) | ||
test.That(t, err, test.ShouldBeNil) | ||
test.That(t, jsonUD, test.ShouldResemble, jsonTD) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
go test fuzz v1 | ||
string("0") |