-
-
Notifications
You must be signed in to change notification settings - Fork 6
/
parser.go
132 lines (110 loc) · 2.11 KB
/
parser.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package frontmatter
import (
"bufio"
"bytes"
"io"
)
type parser struct {
reader *bufio.Reader
output *bytes.Buffer
read int
start int
end int
}
func newParser(r io.Reader) *parser {
return &parser{
reader: bufio.NewReader(r),
output: bytes.NewBuffer(nil),
}
}
func (p *parser) parse(v interface{}, formats []*Format,
mustParse bool) ([]byte, error) {
// If no formats are provided, use the default ones.
if len(formats) == 0 {
formats = defaultFormats()
}
// Detect format.
f, err := p.detect(formats)
if err != nil {
return nil, err
}
// Extract front matter.
found := f != nil
if found {
if found, err = p.extract(f, v); err != nil {
return nil, err
}
}
if mustParse && !found {
return nil, ErrNotFound
}
// Read remaining data.
if _, err := p.output.ReadFrom(p.reader); err != nil {
return nil, err
}
return p.output.Bytes()[p.end:], nil
}
func (p *parser) detect(formats []*Format) (*Format, error) {
for {
read := p.read
line, atEOF, err := p.readLine()
if err != nil || atEOF {
return nil, err
}
if line == "" {
continue
}
for _, f := range formats {
if f.Start == line {
if !f.UnmarshalDelims {
read = p.read
}
p.start = read
return f, nil
}
}
return nil, nil
}
}
func (p *parser) extract(f *Format, v interface{}) (bool, error) {
for {
read := p.read
line, atEOF, err := p.readLine()
if err != nil {
return false, err
}
CheckLine:
if line != f.End {
if atEOF {
return false, err
}
continue
}
if f.RequiresNewLine {
if line, atEOF, err = p.readLine(); err != nil {
return false, err
}
if line != "" {
goto CheckLine
}
}
if f.UnmarshalDelims {
read = p.read
}
if err := f.Unmarshal(p.output.Bytes()[p.start:read], v); err != nil {
return false, err
}
p.end = p.read
return true, nil
}
}
func (p *parser) readLine() (string, bool, error) {
line, err := p.reader.ReadBytes('\n')
atEOF := err == io.EOF
if err != nil && !atEOF {
return "", false, err
}
p.read += len(line)
_, err = p.output.Write(line)
return string(bytes.TrimSpace(line)), atEOF, err
}