
His PR cam during the middle of this development cycle, and it was easier to burn it down and recreate it than try to patch it into an existing series and re-test every assumption. This behavior will be re-introduced in subsequent commits.
148 lines
4.0 KiB
Go
148 lines
4.0 KiB
Go
/*
|
|
Copyright 2014 The Kubernetes Authors.
|
|
|
|
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 intstr
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/google/gofuzz"
|
|
)
|
|
|
|
// IntOrString is a type that can hold an int32 or a string. When used in
|
|
// JSON or YAML marshalling and unmarshalling, it produces or consumes the
|
|
// inner type. This allows you to have, for example, a JSON field that can
|
|
// accept a name or number.
|
|
// TODO: Rename to Int32OrString
|
|
//
|
|
// +protobuf=true
|
|
// +protobuf.options.(gogoproto.goproto_stringer)=false
|
|
type IntOrString struct {
|
|
Type Type `protobuf:"varint,1,opt,name=type,casttype=Type"`
|
|
IntVal int32 `protobuf:"varint,2,opt,name=intVal"`
|
|
StrVal string `protobuf:"bytes,3,opt,name=strVal"`
|
|
}
|
|
|
|
// Type represents the stored type of IntOrString.
|
|
type Type int
|
|
|
|
const (
|
|
Int Type = iota // The IntOrString holds an int.
|
|
String // The IntOrString holds a string.
|
|
)
|
|
|
|
// FromInt creates an IntOrString object with an int32 value. It is
|
|
// your responsibility not to call this method with a value greater
|
|
// than int32.
|
|
// TODO: convert to (val int32)
|
|
func FromInt(val int) IntOrString {
|
|
return IntOrString{Type: Int, IntVal: int32(val)}
|
|
}
|
|
|
|
// FromString creates an IntOrString object with a string value.
|
|
func FromString(val string) IntOrString {
|
|
return IntOrString{Type: String, StrVal: val}
|
|
}
|
|
|
|
// UnmarshalJSON implements the json.Unmarshaller interface.
|
|
func (intstr *IntOrString) UnmarshalJSON(value []byte) error {
|
|
if value[0] == '"' {
|
|
intstr.Type = String
|
|
return json.Unmarshal(value, &intstr.StrVal)
|
|
}
|
|
intstr.Type = Int
|
|
return json.Unmarshal(value, &intstr.IntVal)
|
|
}
|
|
|
|
// String returns the string value, or the Itoa of the int value.
|
|
func (intstr *IntOrString) String() string {
|
|
if intstr.Type == String {
|
|
return intstr.StrVal
|
|
}
|
|
return strconv.Itoa(intstr.IntValue())
|
|
}
|
|
|
|
// IntValue returns the IntVal if type Int, or if
|
|
// it is a String, will attempt a conversion to int.
|
|
func (intstr *IntOrString) IntValue() int {
|
|
if intstr.Type == String {
|
|
i, _ := strconv.Atoi(intstr.StrVal)
|
|
return i
|
|
}
|
|
return int(intstr.IntVal)
|
|
}
|
|
|
|
// MarshalJSON implements the json.Marshaller interface.
|
|
func (intstr IntOrString) MarshalJSON() ([]byte, error) {
|
|
switch intstr.Type {
|
|
case Int:
|
|
return json.Marshal(intstr.IntVal)
|
|
case String:
|
|
return json.Marshal(intstr.StrVal)
|
|
default:
|
|
return []byte{}, fmt.Errorf("impossible IntOrString.Type")
|
|
}
|
|
}
|
|
|
|
func (intstr *IntOrString) Fuzz(c fuzz.Continue) {
|
|
if intstr == nil {
|
|
return
|
|
}
|
|
if c.RandBool() {
|
|
intstr.Type = Int
|
|
c.Fuzz(&intstr.IntVal)
|
|
intstr.StrVal = ""
|
|
} else {
|
|
intstr.Type = String
|
|
intstr.IntVal = 0
|
|
c.Fuzz(&intstr.StrVal)
|
|
}
|
|
}
|
|
|
|
func GetValueFromIntOrPercent(intOrPercent *IntOrString, total int, roundUp bool) (int, error) {
|
|
value, isPercent, err := getIntOrPercentValue(intOrPercent)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("invalid value for IntOrString: %v", err)
|
|
}
|
|
if isPercent {
|
|
if roundUp {
|
|
value = int(math.Ceil(float64(value) * (float64(total)) / 100))
|
|
} else {
|
|
value = int(math.Floor(float64(value) * (float64(total)) / 100))
|
|
}
|
|
}
|
|
return value, nil
|
|
}
|
|
|
|
func getIntOrPercentValue(intOrStr *IntOrString) (int, bool, error) {
|
|
switch intOrStr.Type {
|
|
case Int:
|
|
return intOrStr.IntValue(), false, nil
|
|
case String:
|
|
s := strings.Replace(intOrStr.StrVal, "%", "", -1)
|
|
v, err := strconv.Atoi(s)
|
|
if err != nil {
|
|
return 0, false, fmt.Errorf("invalid value %q: %v", intOrStr.StrVal, err)
|
|
}
|
|
return int(v), true, nil
|
|
}
|
|
return 0, false, fmt.Errorf("invalid type: neither int nor percentage")
|
|
}
|