go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/data/text/pattern/pattern.go (about) 1 // Copyright 2015 The LUCI Authors. 2 // 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 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package pattern implements lightweight parsable string patterns. 16 package pattern 17 18 import ( 19 "fmt" 20 "regexp" 21 "strings" 22 ) 23 24 // Pattern can either match or not match a string. 25 type Pattern interface { 26 // String returns the definition of the pattern parsable by Parse. 27 String() string 28 // Match returns true if s matches this pattern, otherwise false. 29 Match(s string) bool 30 } 31 32 // Parse parses a pattern. 33 // 34 // Ordered by precedence, s can be: 35 // 36 // - "": matches nothing 37 // - "*": matches anything 38 // - "<S>" where S does not have a colon: same as "exact:<S>" 39 // - "exact:<S>": matches only string S 40 // - "text:<S>": same as "exact:<S>" for backward compatibility 41 // - "regex:<E>": matches all strings matching regular expression E. If E 42 // does not start/end with ^/$, they are added automatically. 43 // 44 // Anything else will cause an error. 45 func Parse(s string) (Pattern, error) { 46 switch s { 47 case "": 48 return None, nil 49 case "*": 50 return Any, nil 51 } 52 53 parts := strings.SplitN(s, ":", 2) 54 if len(parts) < 2 { 55 return Exact(s), nil 56 } 57 58 kind, value := parts[0], parts[1] 59 switch kind { 60 case "exact", "text": 61 return Exact(value), nil 62 case "regex": 63 switch value { 64 case ".", ".*": 65 return Any, nil 66 case "^$": 67 return None, nil 68 default: 69 if !strings.HasPrefix(value, "^") { 70 value = "^" + value 71 } 72 if !strings.HasSuffix(value, "$") { 73 value = value + "$" 74 } 75 r, err := regexp.Compile(value) 76 if err != nil { 77 return nil, err 78 } 79 return Regexp(r), nil 80 } 81 default: 82 return nil, fmt.Errorf("unknown pattern kind: %q", kind) 83 } 84 } 85 86 // MustParse parses the pattern according to the specification 87 // of Parse. In addition, it panics if there is an error in parsing the 88 // given string as a pattern. 89 // 90 // See Parse for more details. 91 func MustParse(s string) Pattern { 92 pattern, err := Parse(s) 93 if err != nil { 94 panic(err) 95 } 96 return pattern 97 }