master
go 77 lines 1.56 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package logs
4
5 import (
6 "bufio"
7 "errors"
8 "fmt"
9 "io"
10 "regexp"
11 "slices"
12 )
13
14 type (
15 RegExpConfig struct {
16 Pattern string `yaml:"pattern" json:"pattern"`
17 }
18
19 RegExpParser struct {
20 r *bufio.Reader
21 pattern *regexp.Regexp
22 }
23 )
24
25 func NewRegExpParser(config RegExpConfig, in io.Reader) (*RegExpParser, error) {
26 if config.Pattern == "" {
27 return nil, errors.New("empty pattern")
28 }
29
30 pattern, err := regexp.Compile(config.Pattern)
31 if err != nil {
32 return nil, fmt.Errorf("compile: %w", err)
33 }
34
35 if hasNamed := slices.ContainsFunc(pattern.SubexpNames(), func(s string) bool { return s != "" }); !hasNamed {
36 return nil, errors.New("pattern has no named subgroups")
37 }
38
39 p := &RegExpParser{
40 r: bufio.NewReader(in),
41 pattern: pattern,
42 }
43 return p, nil
44 }
45
46 func (p *RegExpParser) ReadLine(line LogLine) error {
47 row, err := p.r.ReadSlice('\n')
48 if err != nil && len(row) == 0 {
49 return err
50 }
51 if len(row) > 0 && row[len(row)-1] == '\n' {
52 row = row[:len(row)-1]
53 }
54 return p.Parse(row, line)
55 }
56
57 func (p *RegExpParser) Parse(row []byte, line LogLine) error {
58 match := p.pattern.FindSubmatch(row)
59 if len(match) == 0 {
60 return &ParseError{msg: "regexp parse: unmatched line"}
61 }
62
63 for i, name := range p.pattern.SubexpNames() {
64 if name == "" || match[i] == nil {
65 continue
66 }
67 err := line.Assign(name, string(match[i]))
68 if err != nil {
69 return &ParseError{msg: fmt.Sprintf("regexp parse: %v", err), err: err}
70 }
71 }
72 return nil
73 }
74
75 func (p RegExpParser) Info() string {
76 return fmt.Sprintf("regexp: %s", p.pattern)
77 }