github.com/tomwright/dasel@v1.27.3/selector.go (about) 1 package dasel 2 3 import ( 4 "errors" 5 "strings" 6 ) 7 8 // ErrDynamicSelectorBracketMismatch is returned when the number of opening brackets doesn't equal that 9 // of the closing brackets. 10 var ErrDynamicSelectorBracketMismatch = errors.New("dynamic selector bracket mismatch") 11 12 // ExtractNextSelector returns the next selector from the given input. 13 func ExtractNextSelector(input string) (string, int) { 14 escapedIndex := -1 15 res := "" 16 i := 0 17 read := 0 18 for k, v := range input { 19 curRuneStr := string(v) 20 curRuneLength := len(curRuneStr) 21 if escapedIndex == k-1 && k != 0 { 22 // last character was escape character 23 res += curRuneStr 24 read += curRuneLength 25 continue 26 } 27 28 if v == '(' || v == '[' { 29 i++ 30 } else if v == ')' || v == ']' { 31 i-- 32 } 33 34 if v == '\\' { 35 escapedIndex = k 36 read += curRuneLength 37 continue 38 } 39 40 if i == 0 && v == '.' && k != 0 { 41 break 42 } 43 res += curRuneStr 44 read += curRuneLength 45 } 46 return res, read 47 } 48 49 // DynamicSelectorToGroups takes a dynamic selector and splits it into groups. 50 func DynamicSelectorToGroups(selector string) ([]string, error) { 51 i := 0 52 tmp := "" 53 res := make([]string, 0) 54 for k, v := range selector { 55 if v == '(' { 56 if i > 0 { 57 tmp += string(v) 58 } else { 59 tmp = "" 60 } 61 i++ 62 } else if v == ')' { 63 i-- 64 if i == 0 { 65 res = append(res, tmp) 66 tmp = "" 67 } else { 68 tmp += string(v) 69 } 70 } else if v == '.' && i == 0 && k != 0 { 71 return res, nil 72 } else { 73 tmp += string(v) 74 } 75 } 76 if i != 0 { 77 return nil, ErrDynamicSelectorBracketMismatch 78 } 79 return res, nil 80 } 81 82 // DynamicSelectorParts contains the parts for a dynamic selector. 83 type DynamicSelectorParts struct { 84 Key string 85 Comparison string 86 Value string 87 } 88 89 var comparisons = []string{ 90 "=", 91 "!=", 92 "<", 93 "<=", 94 ">", 95 ">=", 96 } 97 98 func isBuildingComparison(comparison string) bool { 99 for _, c := range comparisons { 100 if strings.HasPrefix(c, comparison) { 101 return true 102 } 103 } 104 return false 105 } 106 107 func isValidComparison(comparison string) bool { 108 for _, c := range comparisons { 109 if comparison == c { 110 return true 111 } 112 } 113 return false 114 } 115 116 // FindDynamicSelectorParts extracts the parts from the dynamic selector given. 117 func FindDynamicSelectorParts(selector string) DynamicSelectorParts { 118 i := 0 119 parts := DynamicSelectorParts{} 120 for _, v := range selector { 121 switch { 122 123 // Start of a group 124 case v == '(': 125 if parts.Comparison == "" { 126 parts.Key += string(v) 127 } else { 128 parts.Value += string(v) 129 } 130 i++ 131 132 // End of a group 133 case v == ')': 134 i-- 135 if parts.Comparison == "" { 136 parts.Key += string(v) 137 } else { 138 parts.Value += string(v) 139 } 140 141 // Matches a comparison operator 142 case i == 0 && isValidComparison(parts.Comparison+string(v)): 143 parts.Comparison += string(v) 144 145 // Is building a comparison character 146 case i == 0 && isBuildingComparison(parts.Comparison+string(v)): 147 parts.Comparison += string(v) 148 149 // Add to key or value based on comparison existence 150 default: 151 if parts.Comparison == "" { 152 parts.Key += string(v) 153 } else { 154 parts.Value += string(v) 155 } 156 } 157 } 158 return parts 159 }