github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/logql/log/pattern/pattern.go (about) 1 package pattern 2 3 import ( 4 "bytes" 5 "errors" 6 ) 7 8 var ( 9 ErrNoCapture = errors.New("at least one capture is required") 10 ErrInvalidExpr = errors.New("invalid expression") 11 ) 12 13 type Matcher interface { 14 Matches(in []byte) [][]byte 15 Names() []string 16 } 17 18 type matcher struct { 19 e expr 20 21 captures [][]byte 22 names []string 23 } 24 25 func New(in string) (Matcher, error) { 26 e, err := parseExpr(in) 27 if err != nil { 28 return nil, err 29 } 30 if err := e.validate(); err != nil { 31 return nil, err 32 } 33 return &matcher{ 34 e: e, 35 captures: make([][]byte, 0, e.captureCount()), 36 names: e.captures(), 37 }, nil 38 } 39 40 // Matches matches the given line with the provided pattern. 41 // Matches invalidates the previous returned captures array. 42 func (m *matcher) Matches(in []byte) [][]byte { 43 if len(in) == 0 { 44 return nil 45 } 46 if len(m.e) == 0 { 47 return nil 48 } 49 captures := m.captures[:0] 50 expr := m.e 51 if ls, ok := expr[0].(literals); ok { 52 i := bytes.Index(in, ls) 53 if i != 0 { 54 return nil 55 } 56 in = in[len(ls):] 57 expr = expr[1:] 58 } 59 if len(expr) == 0 { 60 return nil 61 } 62 // from now we have capture - literals - capture ... (literals)? 63 for len(expr) != 0 { 64 if len(expr) == 1 { // we're ending on a capture. 65 if !(expr[0].(capture)).isUnamed() { 66 captures = append(captures, in) 67 } 68 return captures 69 } 70 cap := expr[0].(capture) 71 ls := expr[1].(literals) 72 expr = expr[2:] 73 i := bytes.Index(in, ls) 74 if i == -1 { 75 // if a capture is missed we return up to the end as the capture. 76 if !cap.isUnamed() { 77 captures = append(captures, in) 78 } 79 return captures 80 } 81 82 if cap.isUnamed() { 83 in = in[len(ls)+i:] 84 continue 85 } 86 captures = append(captures, in[:i]) 87 in = in[len(ls)+i:] 88 } 89 90 return captures 91 } 92 93 func (m *matcher) Names() []string { 94 return m.names 95 }