mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
tstime/rate: implement Value.{Marshal,Unmarshal}JSON (#8481)
Implement support for marshaling and unmarshaling a Value. Updates tailscale/corp#8427 Signed-off-by: Joe Tsai <joetsai@digital-static.net>
This commit is contained in:
parent
1c3c3d6752
commit
7732377cd7
@ -4,6 +4,7 @@
|
|||||||
package rate
|
package rate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
@ -181,3 +182,41 @@ func (r *Value) rateNow(now mono.Time) float64 {
|
|||||||
func (r *Value) normalizedIntegral() float64 {
|
func (r *Value) normalizedIntegral() float64 {
|
||||||
return r.halfLife() / math.Ln2
|
return r.halfLife() / math.Ln2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type jsonValue struct {
|
||||||
|
// TODO: Use v2 "encoding/json" for native time.Duration formatting.
|
||||||
|
HalfLife string `json:"halfLife,omitempty,omitzero"`
|
||||||
|
Value float64 `json:"value,omitempty,omitzero"`
|
||||||
|
Updated mono.Time `json:"updated,omitempty,omitzero"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Value) MarshalJSON() ([]byte, error) {
|
||||||
|
if r == nil {
|
||||||
|
return []byte("null"), nil
|
||||||
|
}
|
||||||
|
r.mu.Lock()
|
||||||
|
defer r.mu.Unlock()
|
||||||
|
v := jsonValue{Value: r.value, Updated: r.updated}
|
||||||
|
if r.HalfLife > 0 {
|
||||||
|
v.HalfLife = r.HalfLife.String()
|
||||||
|
}
|
||||||
|
return json.Marshal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Value) UnmarshalJSON(b []byte) error {
|
||||||
|
var v jsonValue
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
halfLife, err := time.ParseDuration(v.HalfLife)
|
||||||
|
if err != nil && v.HalfLife != "" {
|
||||||
|
return fmt.Errorf("invalid halfLife: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.mu.Lock()
|
||||||
|
defer r.mu.Unlock()
|
||||||
|
r.HalfLife = halfLife
|
||||||
|
r.value = v.Value
|
||||||
|
r.updated = v.Updated
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -6,12 +6,14 @@
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"math"
|
"math"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
qt "github.com/frankban/quicktest"
|
qt "github.com/frankban/quicktest"
|
||||||
"github.com/google/go-cmp/cmp/cmpopts"
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
"tailscale.com/tstime/mono"
|
"tailscale.com/tstime/mono"
|
||||||
|
"tailscale.com/util/must"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -234,3 +236,26 @@ func BenchmarkValue(b *testing.B) {
|
|||||||
v.Add(1)
|
v.Add(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValueMarshal(t *testing.T) {
|
||||||
|
now := mono.Now()
|
||||||
|
tests := []struct {
|
||||||
|
val *Value
|
||||||
|
str string
|
||||||
|
}{
|
||||||
|
{val: &Value{}, str: `{}`},
|
||||||
|
{val: &Value{HalfLife: 5 * time.Minute}, str: `{"halfLife":"` + (5 * time.Minute).String() + `"}`},
|
||||||
|
{val: &Value{value: 12345, updated: now}, str: `{"value":12345,"updated":` + string(must.Get(now.MarshalJSON())) + `}`},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
str := string(must.Get(tt.val.MarshalJSON()))
|
||||||
|
if str != tt.str {
|
||||||
|
t.Errorf("string mismatch: got %v, want %v", str, tt.str)
|
||||||
|
}
|
||||||
|
var val Value
|
||||||
|
must.Do(val.UnmarshalJSON([]byte(str)))
|
||||||
|
if !reflect.DeepEqual(&val, tt.val) {
|
||||||
|
t.Errorf("value mismatch: %+v, want %+v", &val, tt.val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user