pathelement.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /*
  2. Copyright 2018 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 internal
  14. import (
  15. "encoding/json"
  16. "errors"
  17. "fmt"
  18. "strconv"
  19. "strings"
  20. "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
  21. "sigs.k8s.io/structured-merge-diff/v4/value"
  22. )
  23. const (
  24. // Field indicates that the content of this path element is a field's name
  25. Field = "f"
  26. // Value indicates that the content of this path element is a field's value
  27. Value = "v"
  28. // Index indicates that the content of this path element is an index in an array
  29. Index = "i"
  30. // Key indicates that the content of this path element is a key value map
  31. Key = "k"
  32. // Separator separates the type of a path element from the contents
  33. Separator = ":"
  34. )
  35. // NewPathElement parses a serialized path element
  36. func NewPathElement(s string) (fieldpath.PathElement, error) {
  37. split := strings.SplitN(s, Separator, 2)
  38. if len(split) < 2 {
  39. return fieldpath.PathElement{}, fmt.Errorf("missing colon: %v", s)
  40. }
  41. switch split[0] {
  42. case Field:
  43. return fieldpath.PathElement{
  44. FieldName: &split[1],
  45. }, nil
  46. case Value:
  47. val, err := value.FromJSON([]byte(split[1]))
  48. if err != nil {
  49. return fieldpath.PathElement{}, err
  50. }
  51. return fieldpath.PathElement{
  52. Value: &val,
  53. }, nil
  54. case Index:
  55. i, err := strconv.Atoi(split[1])
  56. if err != nil {
  57. return fieldpath.PathElement{}, err
  58. }
  59. return fieldpath.PathElement{
  60. Index: &i,
  61. }, nil
  62. case Key:
  63. kv := map[string]json.RawMessage{}
  64. err := json.Unmarshal([]byte(split[1]), &kv)
  65. if err != nil {
  66. return fieldpath.PathElement{}, err
  67. }
  68. fields := value.FieldList{}
  69. for k, v := range kv {
  70. b, err := json.Marshal(v)
  71. if err != nil {
  72. return fieldpath.PathElement{}, err
  73. }
  74. val, err := value.FromJSON(b)
  75. if err != nil {
  76. return fieldpath.PathElement{}, err
  77. }
  78. fields = append(fields, value.Field{
  79. Name: k,
  80. Value: val,
  81. })
  82. }
  83. return fieldpath.PathElement{
  84. Key: &fields,
  85. }, nil
  86. default:
  87. // Ignore unknown key types
  88. return fieldpath.PathElement{}, nil
  89. }
  90. }
  91. // PathElementString serializes a path element
  92. func PathElementString(pe fieldpath.PathElement) (string, error) {
  93. switch {
  94. case pe.FieldName != nil:
  95. return Field + Separator + *pe.FieldName, nil
  96. case pe.Key != nil:
  97. kv := map[string]json.RawMessage{}
  98. for _, k := range *pe.Key {
  99. b, err := value.ToJSON(k.Value)
  100. if err != nil {
  101. return "", err
  102. }
  103. m := json.RawMessage{}
  104. err = json.Unmarshal(b, &m)
  105. if err != nil {
  106. return "", err
  107. }
  108. kv[k.Name] = m
  109. }
  110. b, err := json.Marshal(kv)
  111. if err != nil {
  112. return "", err
  113. }
  114. return Key + ":" + string(b), nil
  115. case pe.Value != nil:
  116. b, err := value.ToJSON(*pe.Value)
  117. if err != nil {
  118. return "", err
  119. }
  120. return Value + ":" + string(b), nil
  121. case pe.Index != nil:
  122. return Index + ":" + strconv.Itoa(*pe.Index), nil
  123. default:
  124. return "", errors.New("Invalid type of path element")
  125. }
  126. }