forked from AdguardTeam/AdGuardHome
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
home: introduce marshalable duration
- Loading branch information
1 parent
bfb1a57
commit 64b00fd
Showing
7 changed files
with
213 additions
and
28 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
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
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
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
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
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,29 @@ | ||
package home | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/AdguardTeam/golibs/errors" | ||
) | ||
|
||
// Duration is a wrapper of time.Duration extending it's functionality. | ||
type Duration struct { | ||
// time.Duration is embedded here to avoid implementing all the methods. | ||
time.Duration | ||
} | ||
|
||
// MarshalText implements the encoding.TextMarshaler interface for Duration. | ||
func (d Duration) MarshalText() (text []byte, err error) { | ||
return []byte(d.String()), nil | ||
} | ||
|
||
const unmarshalAnnotation = "unmarshalling duration:" | ||
|
||
// UnmarshalText implements the encoding.TextUnmarshaler interface for Duration. | ||
func (d *Duration) UnmarshalText(b []byte) (err error) { | ||
defer func() { err = errors.Annotate(err, unmarshalAnnotation+" %w") }() | ||
|
||
d.Duration, err = time.ParseDuration(string(b)) | ||
|
||
return err | ||
} |
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,164 @@ | ||
package home | ||
|
||
import ( | ||
"encoding" | ||
"encoding/json" | ||
"encoding/xml" | ||
"fmt" | ||
"io" | ||
"strings" | ||
"testing" | ||
"time" | ||
|
||
"github.com/AdguardTeam/golibs/errors" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"gopkg.in/yaml.v2" | ||
) | ||
|
||
const ( | ||
NotTextMarshalerErr errors.Error = "not a text marshaler" | ||
NotTextUnmarshalerErr errors.Error = "not a text unmarshaler" | ||
) | ||
|
||
type directTextEncoder struct { | ||
w io.Writer | ||
r io.Reader | ||
} | ||
|
||
func (e *directTextEncoder) Encode(v interface{}) (err error) { | ||
val, ok := v.(encoding.TextMarshaler) | ||
if !ok { | ||
return NotTextMarshalerErr | ||
} | ||
|
||
var data []byte | ||
data, err = val.MarshalText() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
_, err = e.w.Write(data) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (e *directTextEncoder) Decode(v interface{}) (err error) { | ||
val, ok := v.(encoding.TextUnmarshaler) | ||
if !ok { | ||
return NotTextUnmarshalerErr | ||
} | ||
|
||
var data []byte | ||
data, err = io.ReadAll(e.r) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = val.UnmarshalText(data) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
const ( | ||
val = 1 * time.Millisecond | ||
valStr = "1ms" | ||
) | ||
|
||
func TestDuration_MarshalText(t *testing.T) { | ||
d := Duration{val} | ||
b := &strings.Builder{} | ||
|
||
testCases := []struct { | ||
enc interface { | ||
Encode(v interface{}) (err error) | ||
} | ||
name string | ||
fmtStr string | ||
}{{ | ||
enc: yaml.NewEncoder(b), | ||
name: "yaml", | ||
fmtStr: "%s\n", | ||
}, { | ||
enc: json.NewEncoder(b), | ||
name: "json", | ||
fmtStr: "%q\n", | ||
}, { | ||
enc: xml.NewEncoder(b), | ||
name: "xml", | ||
fmtStr: "<Duration>%s</Duration>", | ||
}, { | ||
enc: &directTextEncoder{ | ||
w: b, | ||
}, | ||
name: "direct", | ||
fmtStr: "%s", | ||
}} | ||
|
||
for _, tc := range testCases { | ||
b.Reset() | ||
t.Run(tc.name, func(t *testing.T) { | ||
err := tc.enc.Encode(d) | ||
require.NoError(t, err) | ||
|
||
assert.Equal(t, fmt.Sprintf(tc.fmtStr, val), b.String()) | ||
}) | ||
} | ||
} | ||
|
||
func TestDuration_UnmarshalText(t *testing.T) { | ||
d := Duration{} | ||
|
||
testCases := []struct { | ||
dec interface { | ||
Decode(v interface{}) (err error) | ||
} | ||
name string | ||
}{{ | ||
dec: yaml.NewDecoder( | ||
strings.NewReader(valStr), | ||
), | ||
name: "yaml", | ||
}, { | ||
dec: json.NewDecoder( | ||
strings.NewReader(`"` + valStr + `"`), | ||
), | ||
name: "json", | ||
}, { | ||
dec: xml.NewDecoder( | ||
strings.NewReader("<Duration>" + valStr + "</Duration>"), | ||
), | ||
name: "xml", | ||
}, { | ||
dec: &directTextEncoder{ | ||
r: strings.NewReader(valStr), | ||
}, | ||
name: "direct", | ||
}} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
err := tc.dec.Decode(&d) | ||
require.NoError(t, err) | ||
|
||
assert.Equal(t, val, d.Duration) | ||
}) | ||
} | ||
|
||
t.Run("bad_data", func(t *testing.T) { | ||
dec := &directTextEncoder{ | ||
r: strings.NewReader("abc"), | ||
} | ||
|
||
err := dec.Decode(&d) | ||
require.Error(t, err) | ||
|
||
assert.Contains(t, err.Error(), unmarshalAnnotation) | ||
}) | ||
} |