cloudflare/pint
Publicmirrored from https://github.com/cloudflare/pintAvailable
internal/parser/read.go
185lines · modecode
| 1 | package parser |
| 2 | |
| 3 | import ( |
| 4 | "bufio" |
| 5 | "errors" |
| 6 | "io" |
| 7 | "strings" |
| 8 | ) |
| 9 | |
| 10 | type skipMode int |
| 11 | |
| 12 | const ( |
| 13 | skipNone skipMode = iota |
| 14 | skipNextLine |
| 15 | skipBegin |
| 16 | skipEnd |
| 17 | skipCurrentLine |
| 18 | skipFile |
| 19 | ) |
| 20 | |
| 21 | func removeRedundantSpaces(line string) string { |
| 22 | return strings.Join(strings.Fields(line), " ") |
| 23 | } |
| 24 | |
| 25 | func 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 |
| 40 | } |
| 41 | |
| 42 | func 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 | |
| 58 | func 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 | |
| 75 | func ReadContent(r io.Reader) (out []byte, err error) { |
| 76 | reader := bufio.NewReader(r) |
| 77 | var line string |
| 78 | var found bool |
| 79 | var skip skipMode |
| 80 | |
| 81 | var skipNext bool |
| 82 | var autoReset bool |
| 83 | var skipAll bool |
| 84 | var inBegin bool |
| 85 | for { |
| 86 | line, err = reader.ReadString('\n') |
| 87 | if err != nil && !errors.Is(err, io.EOF) { |
| 88 | break |
| 89 | } |
| 90 | |
| 91 | if skipAll { |
| 92 | out = append(out, []byte(emptyLine(line))...) |
| 93 | } else { |
| 94 | skip, found = parseSkipComment(line) |
| 95 | switch { |
| 96 | case found: |
| 97 | switch skip { |
| 98 | case skipNone: |
| 99 | // no-op |
| 100 | case skipFile: |
| 101 | out = append(out, []byte(emptyLine(line))...) |
| 102 | skipNext = true |
| 103 | autoReset = false |
| 104 | skipAll = true |
| 105 | case skipCurrentLine: |
| 106 | out = append(out, []byte(emptyLine(line))...) |
| 107 | if !inBegin { |
| 108 | skipNext = false |
| 109 | autoReset = true |
| 110 | } |
| 111 | case skipNextLine: |
| 112 | out = append(out, []byte(line)...) |
| 113 | skipNext = true |
| 114 | autoReset = true |
| 115 | case skipBegin: |
| 116 | out = append(out, []byte(line)...) |
| 117 | skipNext = true |
| 118 | autoReset = false |
| 119 | inBegin = true |
| 120 | case skipEnd: |
| 121 | out = append(out, []byte(line)...) |
| 122 | skipNext = false |
| 123 | autoReset = true |
| 124 | inBegin = false |
| 125 | } |
| 126 | case skipNext: |
| 127 | out = append(out, []byte(emptyLine(line))...) |
| 128 | if autoReset { |
| 129 | skipNext = false |
| 130 | } |
| 131 | default: |
| 132 | out = append(out, []byte(line)...) |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | if err != nil { |
| 137 | break |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | if !errors.Is(err, io.EOF) { |
| 142 | return nil, err |
| 143 | } |
| 144 | |
| 145 | return out, nil |
| 146 | } |
| 147 | |
| 148 | type Comment struct { |
| 149 | Key string |
| 150 | Value string |
| 151 | } |
| 152 | |
| 153 | func (c Comment) String() string { |
| 154 | return c.Key + " " + c.Value |
| 155 | } |
| 156 | |
| 157 | func GetComment(line string, comment ...string) (s Comment, ok bool) { |
| 158 | sc := bufio.NewScanner(strings.NewReader(line)) |
| 159 | for sc.Scan() { |
| 160 | elems := strings.Split(sc.Text(), "#") |
| 161 | lastComment := elems[len(elems)-1] |
| 162 | parts := strings.Split(removeRedundantSpaces(lastComment), " ") |
| 163 | if len(parts) < 2 { |
| 164 | continue |
| 165 | } |
| 166 | keys := make([]string, 0, len(parts)) |
| 167 | values := make([]string, 0, len(parts)) |
| 168 | if parts[0] == "pint" && len(parts) >= len(comment)+1 { |
| 169 | for i, c := range comment { |
| 170 | if parts[i+1] != c { |
| 171 | goto NEXT |
| 172 | } |
| 173 | keys = append(keys, parts[i+1]) |
| 174 | } |
| 175 | for i := len(comment) + 1; i < len(parts); i++ { |
| 176 | values = append(values, parts[i]) |
| 177 | } |
| 178 | ok = true |
| 179 | s.Key = strings.Join(keys, " ") |
| 180 | s.Value = strings.Join(values, " ") |
| 181 | } |
| 182 | NEXT: |
| 183 | } |
| 184 | return |
| 185 | } |
| 186 | |