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
|
package headers
import (
"bufio"
"fmt"
"io"
"net/http"
"strings"
)
// Rule contains headers for a single path.
type Rule struct {
Path string
Headers http.Header
}
// Parse parses the given reader.
func Parse(r io.Reader) (rules []Rule, err error) {
scanner := bufio.NewScanner(r)
current := Rule{}
for scanner.Scan() {
line := scanner.Text()
if line == "" || strings.HasPrefix(strings.TrimLeft(line, " \t"), "#") {
continue
}
if line[0] != ' ' && line[0] != '\t' {
// this is a path line
if current.Headers != nil {
rules = append(rules, current)
}
current = Rule{Path: strings.TrimRight(line, " \t"), Headers: http.Header{}}
} else {
// this is a header line
if current.Path == "" {
return nil, fmt.Errorf("header line without preceding path line: %q", line)
}
name, value, found := strings.Cut(line, ":")
if !found {
return nil, fmt.Errorf("header line without separator: %q", line)
}
current.Headers.Add(strings.TrimSpace(name), strings.TrimSpace(value))
}
}
if current.Headers != nil {
rules = append(rules, current)
}
err = scanner.Err()
return
}
// ParseString parses the given string.
func ParseString(str string) ([]Rule, error) {
return Parse(strings.NewReader(str))
}
// Unparse serializes to the given writer.
func Unparse(w io.Writer, rules []Rule) (err error) {
for _, rule := range rules {
if strings.IndexAny(rule.Path, " \t\n") == 0 {
return fmt.Errorf("invalid path: %v", rule.Path)
}
_, err = fmt.Fprintf(w, "%s\n", rule.Path)
if err != nil {
return
}
for name, values := range rule.Headers {
for _, value := range values {
_, err = fmt.Fprintf(w, "\t%s: %s\n", name, value)
if err != nil {
return
}
}
}
}
return
}
// UnparseString serializes to a string.
func UnparseString(rules []Rule) (result string, err error) {
builder := strings.Builder{}
err = Unparse(&builder, rules)
return builder.String(), err
}
// Panics if there is an error.
func Must[T any](value T, err error) T {
if err != nil {
panic(err)
}
return value
}
|