cloudflare/pint

Public

mirrored from https://github.com/cloudflare/pintAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.40.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

internal/parser/read.go

201lines · modecode

1package parser
2
3import (
4 "bufio"
5 "errors"
6 "io"
7 "strings"
8)
9
10type skipMode int
11
12const (
13 skipNone skipMode = iota
14 skipNextLine
15 skipBegin
16 skipEnd
17 skipCurrentLine
18 skipFile
19)
20
21func removeRedundantSpaces(line string) string {
22 return strings.Join(strings.Fields(line), " ")
23}
24
25func emptyLine(line string) (emptied string) {
26 preComment := strings.TrimSuffix(line, "\n")
27 var comment string
28 if commentStart := strings.IndexRune(line, '#'); commentStart >= 0 {
29 comment = preComment[commentStart:]
30 preComment = preComment[:commentStart]
31 }
32
33 emptied = strings.Repeat(" ", len(preComment)) + comment
34
35 if strings.HasSuffix(line, "\n") {
36 emptied += "\n"
37 }
38
39 return emptied
40}
41
42func hasComment(line, comment string) bool {
43 sc := bufio.NewScanner(strings.NewReader(line))
44 for sc.Scan() {
45 elems := strings.Split(sc.Text(), "#")
46 lastComment := elems[len(elems)-1]
47 parts := strings.SplitN(removeRedundantSpaces(lastComment), " ", 2)
48 if len(parts) < 2 {
49 continue
50 }
51 if parts[0] == "pint" && parts[1] == comment {
52 return true
53 }
54 }
55 return false
56}
57
58func parseSkipComment(line string) (skipMode, bool) {
59 switch {
60 case hasComment(line, "ignore/file"):
61 return skipFile, true
62 case hasComment(line, "ignore/line"):
63 return skipCurrentLine, true
64 case hasComment(line, "ignore/next-line"):
65 return skipNextLine, true
66 case hasComment(line, "ignore/begin"):
67 return skipBegin, true
68 case hasComment(line, "ignore/end"):
69 return skipEnd, true
70 default:
71 return skipNone, false
72 }
73}
74
75type Content struct {
76 Body []byte
77 Ignored bool
78}
79
80func ReadContent(r io.Reader) (out Content, err error) {
81 reader := bufio.NewReader(r)
82 var line string
83 var found bool
84 var skip skipMode
85
86 var skipNext bool
87 var autoReset bool
88 var skipAll bool
89 var inBegin bool
90 for {
91 line, err = reader.ReadString('\n')
92 if err != nil && !errors.Is(err, io.EOF) {
93 break
94 }
95
96 if skipAll {
97 out.Body = append(out.Body, []byte(emptyLine(line))...)
98 } else {
99 skip, found = parseSkipComment(line)
100 switch {
101 case found:
102 switch skip {
103 case skipNone:
104 // no-op
105 case skipFile:
106 out.Ignored = true
107 out.Body = append(out.Body, []byte(emptyLine(line))...)
108 skipNext = true
109 autoReset = false
110 skipAll = true
111 case skipCurrentLine:
112 out.Body = append(out.Body, []byte(emptyLine(line))...)
113 if !inBegin {
114 skipNext = false
115 autoReset = true
116 }
117 case skipNextLine:
118 out.Body = append(out.Body, []byte(line)...)
119 skipNext = true
120 autoReset = true
121 case skipBegin:
122 out.Body = append(out.Body, []byte(line)...)
123 skipNext = true
124 autoReset = false
125 inBegin = true
126 case skipEnd:
127 out.Body = append(out.Body, []byte(line)...)
128 skipNext = false
129 autoReset = true
130 inBegin = false
131 }
132 case skipNext:
133 out.Body = append(out.Body, []byte(emptyLine(line))...)
134 if autoReset {
135 skipNext = false
136 }
137 default:
138 out.Body = append(out.Body, []byte(line)...)
139 }
140 }
141
142 if err != nil {
143 break
144 }
145 }
146
147 if !errors.Is(err, io.EOF) {
148 return out, err
149 }
150
151 return out, nil
152}
153
154type Comment struct {
155 Key string
156 Value string
157}
158
159func (c Comment) String() string {
160 return c.Key + " " + c.Value
161}
162
163func GetComments(text string, comment ...string) (comments []Comment) {
164 sc := bufio.NewScanner(strings.NewReader(text))
165 for sc.Scan() {
166 elems := strings.Split(sc.Text(), "#")
167 lastComment := elems[len(elems)-1]
168 parts := strings.Split(removeRedundantSpaces(lastComment), " ")
169 if len(parts) < 2 {
170 continue
171 }
172 keys := make([]string, 0, len(parts))
173 values := make([]string, 0, len(parts))
174 if parts[0] == "pint" && len(parts) >= len(comment)+1 {
175 for i, c := range comment {
176 if parts[i+1] != c {
177 goto NEXT
178 }
179 keys = append(keys, parts[i+1])
180 }
181 for i := len(comment) + 1; i < len(parts); i++ {
182 values = append(values, parts[i])
183 }
184 comments = append(comments, Comment{
185 Key: strings.Join(keys, " "),
186 Value: strings.Join(values, " "),
187 })
188 }
189 NEXT:
190 }
191
192 return comments
193}
194
195func GetLastComment(text string, comment ...string) (Comment, bool) {
196 comments := GetComments(text, comment...)
197 if len(comments) == 0 {
198 return Comment{}, false
199 }
200 return comments[len(comments)-1], true
201}
202