storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/ellipses/ellipses.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2018 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package ellipses 18 19 import ( 20 "errors" 21 "fmt" 22 "regexp" 23 "strconv" 24 "strings" 25 ) 26 27 var ( 28 // Regex to extract ellipses syntax inputs. 29 regexpEllipses = regexp.MustCompile(`(.*)({[0-9a-z]*\.\.\.[0-9a-z]*})(.*)`) 30 31 // Ellipses constants 32 openBraces = "{" 33 closeBraces = "}" 34 ellipses = "..." 35 ) 36 37 // Parses an ellipses range pattern of following style 38 // `{1...64}` 39 // `{33...64}` 40 func parseEllipsesRange(pattern string) (seq []string, err error) { 41 if !strings.Contains(pattern, openBraces) { 42 return nil, errors.New("Invalid argument") 43 } 44 if !strings.Contains(pattern, closeBraces) { 45 return nil, errors.New("Invalid argument") 46 } 47 48 pattern = strings.TrimPrefix(pattern, openBraces) 49 pattern = strings.TrimSuffix(pattern, closeBraces) 50 51 ellipsesRange := strings.Split(pattern, ellipses) 52 if len(ellipsesRange) != 2 { 53 return nil, errors.New("Invalid argument") 54 } 55 56 var hexadecimal bool 57 var start, end uint64 58 if start, err = strconv.ParseUint(ellipsesRange[0], 10, 64); err != nil { 59 // Look for hexadecimal conversions if any. 60 start, err = strconv.ParseUint(ellipsesRange[0], 16, 64) 61 if err != nil { 62 return nil, err 63 } 64 hexadecimal = true 65 } 66 67 if end, err = strconv.ParseUint(ellipsesRange[1], 10, 64); err != nil { 68 // Look for hexadecimal conversions if any. 69 end, err = strconv.ParseUint(ellipsesRange[1], 16, 64) 70 if err != nil { 71 return nil, err 72 } 73 hexadecimal = true 74 } 75 76 if start > end { 77 return nil, fmt.Errorf("Incorrect range start %d cannot be bigger than end %d", start, end) 78 } 79 80 for i := start; i <= end; i++ { 81 if strings.HasPrefix(ellipsesRange[0], "0") && len(ellipsesRange[0]) > 1 || strings.HasPrefix(ellipsesRange[1], "0") { 82 if hexadecimal { 83 seq = append(seq, fmt.Sprintf(fmt.Sprintf("%%0%dx", len(ellipsesRange[1])), i)) 84 } else { 85 seq = append(seq, fmt.Sprintf(fmt.Sprintf("%%0%dd", len(ellipsesRange[1])), i)) 86 } 87 } else { 88 if hexadecimal { 89 seq = append(seq, fmt.Sprintf("%x", i)) 90 } else { 91 seq = append(seq, fmt.Sprintf("%d", i)) 92 } 93 } 94 } 95 96 return seq, nil 97 } 98 99 // Pattern - ellipses pattern, describes the range and also the 100 // associated prefix and suffixes. 101 type Pattern struct { 102 Prefix string 103 Suffix string 104 Seq []string 105 } 106 107 // argExpander - recursively expands labels into its respective forms. 108 func argExpander(labels [][]string) (out [][]string) { 109 if len(labels) == 1 { 110 for _, v := range labels[0] { 111 out = append(out, []string{v}) 112 } 113 return out 114 } 115 for _, lbl := range labels[0] { 116 rs := argExpander(labels[1:]) 117 for _, rlbls := range rs { 118 r := append(rlbls, []string{lbl}...) 119 out = append(out, r) 120 } 121 } 122 return out 123 } 124 125 // ArgPattern contains a list of patterns provided in the input. 126 type ArgPattern []Pattern 127 128 // Expand - expands all the ellipses patterns in 129 // the given argument. 130 func (a ArgPattern) Expand() [][]string { 131 labels := make([][]string, len(a)) 132 for i := range labels { 133 labels[i] = a[i].Expand() 134 } 135 return argExpander(labels) 136 } 137 138 // Expand - expands a ellipses pattern. 139 func (p Pattern) Expand() []string { 140 var labels []string 141 for i := range p.Seq { 142 switch { 143 case p.Prefix != "" && p.Suffix == "": 144 labels = append(labels, fmt.Sprintf("%s%s", p.Prefix, p.Seq[i])) 145 case p.Suffix != "" && p.Prefix == "": 146 labels = append(labels, fmt.Sprintf("%s%s", p.Seq[i], p.Suffix)) 147 case p.Suffix == "" && p.Prefix == "": 148 labels = append(labels, p.Seq[i]) 149 default: 150 labels = append(labels, fmt.Sprintf("%s%s%s", p.Prefix, p.Seq[i], p.Suffix)) 151 } 152 } 153 return labels 154 } 155 156 // HasEllipses - returns true if input arg has ellipses type pattern. 157 func HasEllipses(args ...string) bool { 158 var ok = true 159 for _, arg := range args { 160 ok = ok && (strings.Count(arg, ellipses) > 0 || (strings.Count(arg, openBraces) > 0 && strings.Count(arg, closeBraces) > 0)) 161 } 162 return ok 163 } 164 165 // ErrInvalidEllipsesFormatFn error returned when invalid ellipses format is detected. 166 var ErrInvalidEllipsesFormatFn = func(arg string) error { 167 return fmt.Errorf("Invalid ellipsis format in (%s), Ellipsis range must be provided in format {N...M} where N and M are positive integers, M must be greater than N, with an allowed minimum range of 4", arg) 168 } 169 170 // FindEllipsesPatterns - finds all ellipses patterns, recursively and parses the ranges numerically. 171 func FindEllipsesPatterns(arg string) (ArgPattern, error) { 172 var patterns []Pattern 173 parts := regexpEllipses.FindStringSubmatch(arg) 174 if len(parts) == 0 { 175 // We throw an error if arg doesn't have any recognizable ellipses pattern. 176 return nil, ErrInvalidEllipsesFormatFn(arg) 177 } 178 179 parts = parts[1:] 180 patternFound := regexpEllipses.MatchString(parts[0]) 181 for patternFound { 182 seq, err := parseEllipsesRange(parts[1]) 183 if err != nil { 184 return patterns, err 185 } 186 patterns = append(patterns, Pattern{ 187 Prefix: "", 188 Suffix: parts[2], 189 Seq: seq, 190 }) 191 parts = regexpEllipses.FindStringSubmatch(parts[0]) 192 if len(parts) > 0 { 193 parts = parts[1:] 194 patternFound = HasEllipses(parts[0]) 195 continue 196 } 197 break 198 } 199 200 if len(parts) > 0 { 201 seq, err := parseEllipsesRange(parts[1]) 202 if err != nil { 203 return patterns, err 204 } 205 206 patterns = append(patterns, Pattern{ 207 Prefix: parts[0], 208 Suffix: parts[2], 209 Seq: seq, 210 }) 211 } 212 213 // Check if any of the prefix or suffixes now have flower braces 214 // left over, in such a case we generally think that there is 215 // perhaps a typo in users input and error out accordingly. 216 for _, pattern := range patterns { 217 if strings.Count(pattern.Prefix, openBraces) > 0 || strings.Count(pattern.Prefix, closeBraces) > 0 { 218 return nil, ErrInvalidEllipsesFormatFn(arg) 219 } 220 if strings.Count(pattern.Suffix, openBraces) > 0 || strings.Count(pattern.Suffix, closeBraces) > 0 { 221 return nil, ErrInvalidEllipsesFormatFn(arg) 222 } 223 } 224 225 return patterns, nil 226 }