master
go 95 lines 2.16 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 "strconv"
11 "unsafe"
12
13 "github.com/Wing924/ltsv"
14 )
15
16 type (
17 LTSVConfig struct {
18 FieldDelimiter string `yaml:"field_delimiter" json:"field_delimiter"`
19 ValueDelimiter string `yaml:"value_delimiter" json:"value_delimiter"`
20 Mapping map[string]string `yaml:"mapping" json:"mapping"`
21 }
22
23 LTSVParser struct {
24 r *bufio.Reader
25 parser ltsv.Parser
26 mapping map[string]string
27 }
28 )
29
30 func NewLTSVParser(config LTSVConfig, in io.Reader) (*LTSVParser, error) {
31 p := ltsv.Parser{
32 FieldDelimiter: ltsv.DefaultParser.FieldDelimiter,
33 ValueDelimiter: ltsv.DefaultParser.ValueDelimiter,
34 StrictMode: false,
35 }
36 if config.FieldDelimiter != "" {
37 if d, err := parseLTSVDelimiter(config.FieldDelimiter); err == nil {
38 p.FieldDelimiter = d
39 }
40 }
41 if config.ValueDelimiter != "" {
42 if d, err := parseLTSVDelimiter(config.ValueDelimiter); err == nil {
43 p.ValueDelimiter = d
44 }
45 }
46 parser := &LTSVParser{
47 r: bufio.NewReader(in),
48 parser: p,
49 mapping: config.Mapping,
50 }
51 return parser, nil
52 }
53
54 func (p *LTSVParser) ReadLine(line LogLine) error {
55 row, err := p.r.ReadSlice('\n')
56 if err != nil && len(row) == 0 {
57 return err
58 }
59 if len(row) > 0 && row[len(row)-1] == '\n' {
60 row = row[:len(row)-1]
61 }
62 return p.Parse(row, line)
63 }
64
65 func (p *LTSVParser) Parse(row []byte, line LogLine) error {
66 err := p.parser.ParseLine(row, func(label []byte, value []byte) error {
67 s := *(*string)(unsafe.Pointer(&label)) // no alloc, same as in fmt.Builder.String()
68 if v, ok := p.mapping[s]; ok {
69 s = v
70 }
71 return line.Assign(s, string(value))
72 })
73 if err != nil {
74 return &ParseError{msg: fmt.Sprintf("ltsv parse: %v", err), err: err}
75 }
76 return nil
77 }
78
79 func (p LTSVParser) Info() string {
80 return fmt.Sprintf("ltsv: %q", p.mapping)
81 }
82
83 func parseLTSVDelimiter(s string) (byte, error) {
84 if isNumber(s) {
85 d, err := strconv.ParseUint(s, 10, 8)
86 if err != nil {
87 return 0, fmt.Errorf("invalid LTSV delimiter: %v", err)
88 }
89 return byte(d), nil
90 }
91 if len(s) != 1 {
92 return 0, errors.New("invalid LTSV delimiter: must be a single character")
93 }
94 return s[0], nil
95 }