-
Notifications
You must be signed in to change notification settings - Fork 0
/
param.go
168 lines (144 loc) · 3.91 KB
/
param.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package param
import (
"errors"
"fmt"
"reflect"
"time"
)
// Param is a value or a struct that has age and validity. Updating a
// Param also fires an event.
type Param struct {
Name string
value interface{}
updated time.Time
params *Params
final bool
}
// Ok return true if the value has been recently updated.
func (p *Param) Ok() bool {
if p.final {
return true
}
elapsed := time.Now().Sub(p.updated)
return elapsed.Seconds() < 3
}
// Get returns the current value which may be nil.
func (p *Param) Get() interface{} {
return p.value
}
// GetFloat64 returns the current value as a float64.
func (p *Param) GetFloat64() float64 {
return p.value.(float64)
}
// GetInt returns the current value as an int.
func (p *Param) GetInt() int {
return int(p.GetFloat64())
}
func asNumber(value interface{}) (float64, error) {
if value == nil {
return 0, errors.New("Is nil")
}
switch value.(type) {
case float64:
return value.(float64), nil
case int:
return float64(value.(int)), nil
default:
return 0, errors.New("Not a number")
}
}
func isNumber(value interface{}) bool {
_, err := asNumber(value)
return err == nil
}
// Set the value, update validity, and notify listeners.
func (p *Param) Set(value interface{}) error {
if p.value == nil {
// OK, nothing set yet.
} else if isNumber(p.value) && isNumber(value) {
// Number -> number is fine.
} else if reflect.TypeOf(value) != reflect.TypeOf(p.value) {
return fmt.Errorf("Type of %v changed from %v to %v",
p.Name, p.value, value)
}
if isNumber(value) {
value, _ = asNumber(value)
}
p.value = value
p.updated = time.Now()
p.final = false
p.params.updated(p)
return nil
}
// Update tries to update the value.
func (p *Param) Update(value interface{}) (bool, error) {
// Currently only handles numbers.
if isNumber(value) && isNumber(p.value) {
right, _ := asNumber(value)
left, _ := asNumber(p.value)
if left == right {
return false, nil
}
}
return true, p.Set(value)
}
// SetFloat64 tries to update the value as a float64.
func (p *Param) SetFloat64(value float64) error {
return p.Set(value)
}
// SetInt tries to update the value as an int.
func (p *Param) SetInt(value int) error {
return p.Set(value)
}
// UpdateInt tries to update the value as an int.
func (p *Param) UpdateInt(value int) (bool, error) {
if p.GetInt() == value {
return false, nil
}
return true, p.Set(value)
}
// Inc tries to increment the integer value.
func (p *Param) Inc() error {
return p.SetInt(p.GetInt() + 1)
}
// Dec tries to decrement the integer value.
func (p *Param) Dec() error {
return p.SetInt(p.GetInt() - 1)
}
// Finalise marks the param as always valid.
func (p *Param) Finalise() {
p.final = true
}
// ValueVisitor is a callback for leaves in the parameter tree.
type ValueVisitor func(p *Param, path []string, value interface{})
func (p *Param) walk(visitor ValueVisitor, path []string, v reflect.Value) {
switch v.Kind() {
case reflect.Ptr, reflect.Interface:
p.walk(visitor, path, v.Elem())
case reflect.Struct:
t := v.Type()
for i := 0; i < v.NumField(); i++ {
p.walk(visitor, append(path, t.Field(i).Name), v.Field(i))
}
default:
visitor(p, path, v.Interface())
}
}
// Walk calls visitor on all leaf values of this parameter.
func (p *Param) Walk(visitor ValueVisitor) {
p.walk(visitor, []string{p.Name}, reflect.ValueOf(p.Get()))
}