intstr.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package intstr
  14. import (
  15. "encoding/json"
  16. "fmt"
  17. "math"
  18. "runtime/debug"
  19. "strconv"
  20. "strings"
  21. "github.com/golang/glog"
  22. "github.com/google/gofuzz"
  23. )
  24. // IntOrString is a type that can hold an int32 or a string. When used in
  25. // JSON or YAML marshalling and unmarshalling, it produces or consumes the
  26. // inner type. This allows you to have, for example, a JSON field that can
  27. // accept a name or number.
  28. // TODO: Rename to Int32OrString
  29. //
  30. // +protobuf=true
  31. // +protobuf.options.(gogoproto.goproto_stringer)=false
  32. // +k8s:openapi-gen=true
  33. type IntOrString struct {
  34. Type Type `protobuf:"varint,1,opt,name=type,casttype=Type"`
  35. IntVal int32 `protobuf:"varint,2,opt,name=intVal"`
  36. StrVal string `protobuf:"bytes,3,opt,name=strVal"`
  37. }
  38. // Type represents the stored type of IntOrString.
  39. type Type int
  40. const (
  41. Int Type = iota // The IntOrString holds an int.
  42. String // The IntOrString holds a string.
  43. )
  44. // FromInt creates an IntOrString object with an int32 value. It is
  45. // your responsibility not to call this method with a value greater
  46. // than int32.
  47. // TODO: convert to (val int32)
  48. func FromInt(val int) IntOrString {
  49. if val > math.MaxInt32 || val < math.MinInt32 {
  50. glog.Errorf("value: %d overflows int32\n%s\n", val, debug.Stack())
  51. }
  52. return IntOrString{Type: Int, IntVal: int32(val)}
  53. }
  54. // FromString creates an IntOrString object with a string value.
  55. func FromString(val string) IntOrString {
  56. return IntOrString{Type: String, StrVal: val}
  57. }
  58. // Parse the given string and try to convert it to an integer before
  59. // setting it as a string value.
  60. func Parse(val string) IntOrString {
  61. i, err := strconv.Atoi(val)
  62. if err != nil {
  63. return FromString(val)
  64. }
  65. return FromInt(i)
  66. }
  67. // UnmarshalJSON implements the json.Unmarshaller interface.
  68. func (intstr *IntOrString) UnmarshalJSON(value []byte) error {
  69. if value[0] == '"' {
  70. intstr.Type = String
  71. return json.Unmarshal(value, &intstr.StrVal)
  72. }
  73. intstr.Type = Int
  74. return json.Unmarshal(value, &intstr.IntVal)
  75. }
  76. // String returns the string value, or the Itoa of the int value.
  77. func (intstr *IntOrString) String() string {
  78. if intstr.Type == String {
  79. return intstr.StrVal
  80. }
  81. return strconv.Itoa(intstr.IntValue())
  82. }
  83. // IntValue returns the IntVal if type Int, or if
  84. // it is a String, will attempt a conversion to int.
  85. func (intstr *IntOrString) IntValue() int {
  86. if intstr.Type == String {
  87. i, _ := strconv.Atoi(intstr.StrVal)
  88. return i
  89. }
  90. return int(intstr.IntVal)
  91. }
  92. // MarshalJSON implements the json.Marshaller interface.
  93. func (intstr IntOrString) MarshalJSON() ([]byte, error) {
  94. switch intstr.Type {
  95. case Int:
  96. return json.Marshal(intstr.IntVal)
  97. case String:
  98. return json.Marshal(intstr.StrVal)
  99. default:
  100. return []byte{}, fmt.Errorf("impossible IntOrString.Type")
  101. }
  102. }
  103. // OpenAPISchemaType is used by the kube-openapi generator when constructing
  104. // the OpenAPI spec of this type.
  105. //
  106. // See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
  107. func (_ IntOrString) OpenAPISchemaType() []string { return []string{"string"} }
  108. // OpenAPISchemaFormat is used by the kube-openapi generator when constructing
  109. // the OpenAPI spec of this type.
  110. func (_ IntOrString) OpenAPISchemaFormat() string { return "int-or-string" }
  111. func (intstr *IntOrString) Fuzz(c fuzz.Continue) {
  112. if intstr == nil {
  113. return
  114. }
  115. if c.RandBool() {
  116. intstr.Type = Int
  117. c.Fuzz(&intstr.IntVal)
  118. intstr.StrVal = ""
  119. } else {
  120. intstr.Type = String
  121. intstr.IntVal = 0
  122. c.Fuzz(&intstr.StrVal)
  123. }
  124. }
  125. func GetValueFromIntOrPercent(intOrPercent *IntOrString, total int, roundUp bool) (int, error) {
  126. value, isPercent, err := getIntOrPercentValue(intOrPercent)
  127. if err != nil {
  128. return 0, fmt.Errorf("invalid value for IntOrString: %v", err)
  129. }
  130. if isPercent {
  131. if roundUp {
  132. value = int(math.Ceil(float64(value) * (float64(total)) / 100))
  133. } else {
  134. value = int(math.Floor(float64(value) * (float64(total)) / 100))
  135. }
  136. }
  137. return value, nil
  138. }
  139. func getIntOrPercentValue(intOrStr *IntOrString) (int, bool, error) {
  140. switch intOrStr.Type {
  141. case Int:
  142. return intOrStr.IntValue(), false, nil
  143. case String:
  144. s := strings.Replace(intOrStr.StrVal, "%", "", -1)
  145. v, err := strconv.Atoi(s)
  146. if err != nil {
  147. return 0, false, fmt.Errorf("invalid value %q: %v", intOrStr.StrVal, err)
  148. }
  149. return int(v), true, nil
  150. }
  151. return 0, false, fmt.Errorf("invalid type: neither int nor percentage")
  152. }